黑马程序员视频地址:
Node.js与Webpack-16.Webpack简介以及体验
前言:
本篇中部分标题后标有数字,代表学习顺序 ,同时也可以作为使用顺序参考
基础认识
使用步骤
下载webpack包和webpack-cli包
注意点:
1.使用npm命令前记得初始化,获取package.json文件
2.在项目路径下 下载webpack和webpack-cli包
(不同项目用到的版本不一样,因此需要下载到当前路径下)
3. --save-dev表示在开发环境下使用,书写位置无要求,左边右边都可以
开发模式:本地开发测试时的环境
生产模式:供用户使用的服务器的环境
npm i webpack webpack-cli --save-dev
配置局部自定义命令(防止用错其他项目中不同版本的webpack)
//package.json
{
//...
"scripts": {
//...
"build": "webpack" //自定义命令:实际命令
},
//...
}
运行自定义命令,进行打包
npm run 自定义命令
修改打包入口和打包出口
默认打包入口是src中的index.js文件, 默认打包出口是dist中的main.js文件
webpack中文文档:
步骤
在项目根目录下创建webpack配置文件webpack.config.js
目标:
1.修改入口文件为:src/login/index.js
2.修改出口文件为:dist/login/index.js
const path = require('path');
module.exports = {
//入口
entry: path.resolve(__dirname, "src/login/index.js"),
//出口
output: {
path: path.resolve(__dirname, 'dist'),
filename: './login/index.js'
},
};
运行结果:
清空输出目录
webpack版本在5.2.0.0及以上时可配置clean: true参数使每次运行时先清空输出目录
const path = require('path');
module.exports = {
//入口
entry: path.resolve(__dirname, "src/login/index.js"),
//出口
output: {
path: path.resolve(__dirname, 'dist/login'),
filename: 'index.js',
clean: true
},
};
结果运行:
插件(Plugin)
插件:使webpack拥有更多功能
❶HtmlWebpackPlugin(生成HTML文件)
该插件会自动将webpack打包的其他文件引入到一个新建的(或自定义的)HTML文件中
此处代码为配置自定义HTML文件(以已有HTML为模板创建HTML文件)
文档:
1.安装命令(报错可以再次运行重新下载)
npm i --save-dev html-webpack-plugin
2.在webpack.config.js中配置
插件对象放在webpack.config.js文件中的plugins数组里
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
//...
plugins: [
new HtmlWebpackPlugin({
template = path.resolve(__dirname, "public/login.html"), //模板文件
filename = path.resolve(__dirname, "dist/login/login.html") //输出文件
})
]
}
HtmlWebpackPlugin对象属性值:
GitHub - jantimon/html-webpack-plugin:简化 HTML 文件的创建,以便为您的 webpack 捆绑包提供服务
注意:github需要使用加速器打开,可以下载Steam++
3.运行打包命令 npm run 自定义命令
❹MiniCssExtractPlugin(提取css代码)
❗❗❗该插件不能与style-loader加载器一起使用
style-loader:把css代码打包到js文件中,并插入到DOM上
MiniCssExtractPlugin:
1.把css代码单独提取出来成一个.css文件
2.默认不会压缩css代码
文档:
1.安装命令
npm i --save-dev mini-css-extract-plugin
2.在webpack.config.js中配置
//...
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
//...
//插件(给webpack提供更多功能)
plugins: [
//...
new MiniCssExtractPlugin() //这里可以传值,但只能传相对路径
],
//加载器(让webpack识别更多模块文件内容)
module: {
rules: [
{
test: /\.css$/i,
use: [MiniCssExtractPlugin.loader, "css-loader"],
},
],
},
};
3.打包
❺CssMinimizerWebpackPlugin(压缩css代码)
MiniCssExtractPlugin插件默认不会压缩css代码,因此需要借助本插件
1.安装命令
npm i css-minimizer-webpack-plugin --save-dev
2.在webpack.config.js文件中配置
在minimizer中原本配置了压缩js的语法,重新配置会导致原来的被覆盖,解决方法如下:
A.在 webpack@5 中,可以使用 `...` 语法来扩展原有配置(推荐)
B.自行下载配置TerserWebpackPlugin插件
//...
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
module.exports = {
//...
//优化打包过程
optimization: {
//最小化
minimizer: [
`...`, //在 webpack@5 中,可以使用 `...` 语法来扩展原有的执行压缩js代码的语法
new CssMinimizerPlugin(),
],
},
};
3.打包
⓫DefinePlugin(前端注入环境变量)
问题:cross-env设置的变量如上文process.env.NODE_ENV只能在node.js中生效,前端代码无法访问
解决:使用该插件将node.js中的变量的值编写进编译的文件中
文档:DefinePlugin | webpack 中文文档
注意:DefinePlugin是webpack内置的插件,无需下载
配置webpack.config.js
原理:该插件会把js文件中的变量直接改写成右边的值并编译
注意:右侧被改写的值的类型得是字符串,不能是变量
如果右侧值是字符串:左侧变量会直接被改写成字符串(我们要这种效果)
如果右侧值是变量:左侧变量会直接被改写成(以右侧变量的值为名称)的变量,若此时该变量的值为未知,会报错
//...
const webpack = require("webpack")
module.exports = {
//...
//插件(给webpack提供更多功能)
plugins: [
//...
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
})
],
//...
};
加载器(Loader)
webpack默认只识别js代码,要识别更多内容要使用加载器
❷css-loader、style-loader(打包css文件)
该加载器用来将css代码进行打包
文档:
1.安装命令
npm i --save-dev css-loader style-loader
2.在src/login/index.js中引入index.css文件让其产生关联性,让webpack打包
import "./index.css"
3.在webpack.config.js文件中配置
在rules中的use数组中使用加载器
//...
module.exports = {
//...
//加载器(让webpack识别更多模块文件内容)
module: {
rules: [
{
test: /\.css$/i, //匹配.css文件,i表示忽略大小写
use: ['style-loader', 'css-loader'], //从后往前使用
},
],
},
};
4.打包
默认生成css代码在js文件中
❸拓展:引入bootstrap包的css文件
1.安装命令
npm i bootstrap
2.引入包(因为要使用css文件,默认入口不是css样式,所以具体到css文件)(建议先引入第三方包)
//src/login/index.js
import "bootstrap/dist/css/bootstrap.min.css"
3.打包
❻less-loader(打包less文件)
打包less文件
文档:
1.安装less和less-loader
npm i less less-loader --save-dev
2.在src/login/index.js中引入index.less文件让其产生关联性,让webpack打包
import "./index.less"
3.配置webpack.config.js文件
//...
module.exports = {
//...
//加载器(让webpack识别更多模块文件内容)
module: {
rules: [
//{
//test: /\.css$/i, //匹配.css文件,i表示忽略大小写
use: ['style-loader', 'css-loader'], //'style-loader'或者下一行的
//use: [MiniCssExtractPlugin.loader, "css-loader"], //MiniCssExtractPlugin.loader
//},
{
test: /\.less$/i,//匹配.less文件
use: [
// compiles Less to CSS
MiniCssExtractPlugin.loader, //需要与css配置中选择一致的
'css-loader',
'less-loader',
],
},
],
},
//...
};
❼资源模块(打包图片、字体)
在webpack@5后,新增了内置的资源模块,可以不使用加载器来打包字体、图片等资源
内置资源模块默认情况下可进行的操作十分局限,如不能使用import导入图片资源
因此需要进一步配置
在webpack.config.js文件中配置
资源模块类型:
asset
在导出一个 data URI (小于8kb)和发送一个单独的文件(大于8kb)之间自动选择。导出一个 data URI:直接把图片转成base64镶嵌在js文件中
发送一个单独的文件:在外部存放图片
另外三种见资源模块 | webpack 中文文档
导出文件路径:
导出文件路径是在配置的出口路径的基础上导出的
✓占位符【hash】对模块内容做算法计算,得到映射的数字字母组合的字符串
✓占位符【ext】使用当前模块原本的占位符,例如:.png / .jpg 等字符串
✓占位符【query】保留引入文件时代码中查询参数(只有URL 下生效)
//...
module.exports = {
//...
//加载器(让webpack识别更多模块文件内容)
module: {
rules: [
//...
{
test: /\.(png|jpg|jpeg|gif)$/i, //匹配的资源类型
type: 'asset', //资源模块类型
generator: {
filename: 'static/[hash][ext][query]' //导出文件路径
}
},
],
},
//...
};
❽拓展:引入axios并配置基地址
1.安装axios包
npm i axios
2.引入axios,配置完基地址后再导出axios
import axios from "axios" //导入axios
// axios 公共配置
// 基地址
axios.defaults.baseURL = 'http://geek.itheima.net'
// 添加请求拦截器
//...
// 添加响应拦截器
//...
export default axios //导出axios,供其他文件使用
3.在使用页面导入后即可使用
import myAxios from "../utils/request.js"
❾开发环境 webpack-dev-server
用来热更新代码
注意:
1.webpack-dev-server 借助 http 模块创建 8080 默认 Web 服务
2.默认以 public 文件夹作为服务器根目录
3.webpack-dev-server 根据配置,打包相关代码在内存当中,以 output.path 的值作为服务器根目录(所以可以直接自己拼接访问 dist 目录下内容)
1.安装 webpack-dev-server 包
npm i --save-dev webpack-dev-server
2.在package.json配置自定义命令
--open:启动serve后自动弹出浏览器,不设置的话运行后终端会自动告诉你浏览器地址
{
//...
"scripts": {
//...
"dev": "webpack serve --open" //dev是自定义的
},
//...
}
3.设置打包模式
development:调试代码,实时加载,模块热替换(快)
production:压缩代码,资源优化,更轻量等(小)
设置方式:
(1):mode 选项设置(在webpack.config.js中配置,极其不推荐,会导致自定义命令build打包时,也变成development模式)
(2):--mode= 命令行设置(优先级高,在package.json中配置,推荐)
//(1)webpack.config.js
//...
module.exports = {
//打包模式(development 开发模式-使用相关内置优化)
mode: "development",
//...
};
//(2)
{
//...
"scripts": {
//...
"build": "webpack --mode=production",
"dev": "webpack serve --open --mode=development"
},
//...
}
注意:
webpack会更新dist文件夹里的文件,是可视化的
webpack serve则是不可视的,在内存中运行
❿灵活切换模式(含cross-env包配置环境变量)
目标:
在开发环境下,使用style-loader使加载速度更快
在生产环境下,使用MiniCssExtractPlugin插件,单独抽离出css文件
方法一:(不推荐)webpack.config.js配置中,不再导出对象,而是导出函数
缺点:局限性大,只支持两种模式
module.exports = (env, argv) => {
if (argv.mode === 'development') {
//开发模式下执行
}
if (argv.mode === 'production') {
//生产模式下执行
}
};
方法二:借助cross-env包,设置参数
cross-env是一个用于跨平台设置和使用环境变量的工具
缺点:需要进行判断,只适用于差异小的模式
1.安装cross-env包
npm i cross-env --save-dev
2.在package.json文件中配置自定义命令,快速传递参数名和值
注意:参数会绑定到node.js中内置的环境变量process.env对象中
{
//...
"scripts": {
//...
//格式:cross-env 变量名1=值1 变量名2=值2(可以传多个)
"build": "cross-env NODE_ENV=production webpack --mode=production",
"dev": "cross-env NODE_ENV=development webpack serve --open --mode=development"
},
//...
}
3.在webpack.config.js中使用变量进行判断
//...
module.exports = {
//...
//加载器
module: {
rules: [
{
//...
use: [process.env.NODE_ENV === "production" ? MiniCssExtractPlugin.loader : "style-loader", "css-loader"],
},
{
//...
use: [
//...
process.env.NODE_ENV === "production" ? MiniCssExtractPlugin.loader : "style-loader",
//...
],
},
//...
],
},
//...
};
方法三:配置不同的webpack.config.js
适用于差异较大的模式
此处暂时不进行讲解
⓬开发环境调错(devtool)
作用:精准定位报错位置
注意:source map应仅在开发环境使用,在生产环境应关闭
配置webpack.config.js
//...
module.exports = {
//...
devtool: "inline-source-map"
}
配合环境变量实现不同模式开关调错
//...
const config = {
//...
};
if(process.env.NODE_ENV === "development")
{
config.devtool = "inline-source-map"
}
module.exports = config
⓭解析别名
在webpack.config.js配置文件中自定义别名来代表绝对路径
//...
const config = {
//...
//解析
resolve: {
//别名
alias: {
"@": path.resolve(__dirname, "src")
}
}
}
使用
import youAxios from "@/utils/request.js"
⓮CDN的使用
定义:内容分发网络,指的是一组分布在各个地区的服务器
作用:把静态资源文件/第三方库放在CDN 网络中各个服务器中,供用户就近请求获取
好处:减轻自己服务器请求压力,就近请求物理延迟低,配套缓存策略
免费的CDN网站:
BootCDN - Bootstrap 中文网开源项目免费 CDN 加速服务 铂特优选
unpkg的使用方法:
直接在网站后面加上 /包名 即可,如:https://unpkg.com/form-serialize
需求:开发模式下使用本地第三方库,生产模式下使用CDN加载引入
1.在需要引入cdn远程模块的HTML文件中加入判断
⭐️待解决问:1:为什么要引入暂时没用到的bootstrap.min.js文件
黑马的解释:import "bootstrap/dist/css/bootstrap.min.css"会在原地留下一个bootstrap全局变量,如果不引入会使浏览器在运行时该变量报错
<% if(htmlWebpackPlugin.options.useCdn){ %>
<link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.3.3/css/bootstrap.min.css">
<% } %>
<!--当括号内为真时才会执行中间的代码-->
<!--而条件控制在webpack.config.js中配置-->
<% if(htmlWebpackPlugin.options.useCdn){ %>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/1.7.8/axios.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.3.3/js/bootstrap.min.js"></script>
<% } %>
2.配置执行上述判断的条件
在webpack.config.js中配置
当useCdn为true时执行上述中间代码
//...
const config = {
...
//插件(给webpack提供更多功能)
plugins: [
new HtmlWebpackPlugin({
//...
useCdn: process.env.NODE_ENV === "production"
}),
//...
],
//...
};
3.配置屏蔽掉引用本地模块的代码
参数externals
键(key)表示模块的导入路径,值(value)表示在全局作用域中可以访问该模块的变量名
import axios from "axios"
import 值1 from "值2"
键:写双引号内的值,值2
值:写import后的值,值1
//...
const config = {
//...
};
if(process.env.NODE_ENV === "production")
{
config.externals = {
//key : value
"axios": "axios",
"bootstrap/dist/css/bootstrap.min.css": "bootstrap"
}
}
module.exports = config
⓯多页面打包
webpack.config.js配置如下
讲解在注释中,标⭐️的
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const webpack = require("webpack")
const config = {
//入口
entry: { //⭐️改成对象形式,一个属性对应一个整合js文件
"login": path.resolve(__dirname, "src/login/index.js"),
"content": path.resolve(__dirname, "src/content/index.js")
},
//出口
output: {
path: path.resolve(__dirname, 'dist'),
filename: './[name]/index.js', //⭐️[name]为占位符,会与entry的属性保持一致
clean: true
},
//插件(给webpack提供更多功能)
plugins: [
new HtmlWebpackPlugin({ //⭐️每一个HTMLwebpackPlugin对象代表了一个HTML页面
template: path.resolve(__dirname, "public/login.html"), //模板文件
filename: path.resolve(__dirname, "dist/login/login.html"), //输出文件
useCdn: process.env.NODE_ENV === "production",
chunks: ["login"] //⭐️标记引入哪一个打包的整合js文件
}),
new HtmlWebpackPlugin({ //⭐️这是第二个页面
template: path.resolve(__dirname, "public/content.html"), //模板文件
filename: path.resolve(__dirname, "dist/content/content.html"), //输出文件
useCdn: process.env.NODE_ENV === "production",
chunks: ["content"]
}),
new MiniCssExtractPlugin({
filename: "./[name]/index.css" //⭐️占位符同理,entry有几个值,就会生产几个css
}),
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
})
],
//加载器(让webpack识别更多模块文件内容)
module: {
rules: [
{
test: /\.css$/i, //匹配.css文件,i表示忽略大小写
//use: ['style-loader', 'css-loader'], //从后往前使用
use: [process.env.NODE_ENV === "production" ? MiniCssExtractPlugin.loader : "style-loader", "css-loader"],
},
{
test: /\.less$/i,//匹配.less文件
use: [
// compiles Less to CSS
process.env.NODE_ENV === "production" ? MiniCssExtractPlugin.loader : "style-loader",
'css-loader',
'less-loader',
],
},
{
test: /\.(png|jpg|jpeg|gif)$/i,
type: 'asset',
generator: {
filename: 'static/[hash][ext][query]'
}
},
],
},
//优化
optimization: {
//最小化
minimizer: [
// 在 webpack@5 中,你可以使用 `...` 语法来扩展现有的 minimizer(即 `terser-webpack-plugin`),将下一行取消注释
`...`,
new CssMinimizerPlugin(),
],
},
//解析
resolve: {
//别名
alias: {
"@": path.resolve(__dirname, "src")
}
},
};
if(process.env.NODE_ENV === "development")
{
config.devtool = "inline-source-map"
}
if(process.env.NODE_ENV === "production")
{
config.externals = {
"axios": "axios",
"bootstrap/dist/css/bootstrap.min.css": "bootstrap"
}
}
module.exports = config
⓰一些特殊情况
1.form-serializeCDN在第二个CDN网站上有
2.form-serialize和wangeditor5等不确定包名是正确,可以在npm | Home查询
如wangeditor5的安装命令为:npm i @wangeditor/editor
3.form-serialize本地包安装命令不需要加--save-dev让其在开发环境,因为生产环境中也要使用
4.wangeditor5只能用require导入
const wangEditor = require("@wangeditor/editor")
⓱分割公共代码(splitChunks)
抽离出公共js代码
//...
const config = {
//...
//优化
optimization: {
//...
//分割公共代码
splitChunks: {
chunks: 'all', // 所有模块动态非动态移入的都分割分析
cacheGroups: { // 分隔组
commons: { // 抽取公共模块
minSize: 0, // 抽取的chunk最小大小字节
minChunks: 2, // 最小引用数
reuseExistingChunk: true, // 当前 chunk 包含已从主 bundle 中拆分出的模块,则它将被重用
name(module, chunks, cacheGroupKey) { // 分离出模块文件名
const allChunksNames = chunks.map((item) => item.name).join('~') // 模块名1~模块名2
return `./js/${allChunksNames}` // 输出到 dist 目录下位置
}
}
}
}
},
//...
};
webpack.config.js完整代码
黑马程序员配置的完整代码
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
const webpack = require('webpack')
const config = {
// 打包模式(development 开发模式-使用相关内置优化)
// mode: 'development',
// 入口
// entry: path.resolve(__dirname, 'src/login/index.js'),
entry: {
'login': path.resolve(__dirname, 'src/login/index.js'),
'content': path.resolve(__dirname, 'src/content/index.js'),
'publish': path.resolve(__dirname, 'src/publish/index.js')
},
// 出口
output: {
path: path.resolve(__dirname, 'dist'),
filename: './[name]/index.js',
clean: true // 生成打包后内容之前,清空输出目录
},
// 插件(给 Webpack 提供更多功能)
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'public/login.html'), // 模板文件
filename: path.resolve(__dirname, 'dist/login/index.html'), // 输出文件
useCdn: process.env.NODE_ENV === 'production', // 生产模式下使用 cdn 引入的地址
chunks: ['login'] // 引入哪些打包后的模块(和 entry 的 key 一致)
}),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'public/content.html'), // 模板文件
filename: path.resolve(__dirname, 'dist/content/index.html'), // 输出文件
useCdn: process.env.NODE_ENV === 'production', // 生产模式下使用 cdn 引入的地址
chunks: ['content']
}),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'public/publish.html'), // 模板文件
filename: path.resolve(__dirname, 'dist/publish/index.html'), // 输出文件
useCdn: process.env.NODE_ENV === 'production', // 生产模式下使用 cdn 引入的地址
chunks: ['publish']
}),
new MiniCssExtractPlugin({
filename: './[name]/index.css'
}), // 生成 css 文件
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
})
],
// 加载器(让 webpack 识别更多模块文件内容)
module: {
rules: [
{
test: /\.css$/i,
// use: ['style-loader', "css-loader"],
use: [process.env.NODE_ENV === 'development' ? 'style-loader' : MiniCssExtractPlugin.loader, "css-loader"]
},
{
test: /\.less$/i,
use: [
// compiles Less to CSS
process.env.NODE_ENV === 'development' ? 'style-loader' : MiniCssExtractPlugin.loader,
'css-loader',
'less-loader',
],
},
{
test: /\.(png|jpg|jpeg|gif)$/i,
type: 'asset',
generator: {
filename: 'assets/[hash][ext][query]'
}
}
],
},
// 优化
optimization: {
// 最小化
minimizer: [
// 在 webpack@5 中,你可以使用 `...` 语法来扩展现有的 minimizer(即 `terser-webpack-plugin`),将下一行取消注释(保证 js 代码还能压缩)
`...`,
new CssMinimizerPlugin(),
],
splitChunks: {
chunks: 'all', // 所有模块动态非动态移入的都分割分析
cacheGroups: { // 分隔组
commons: { // 抽取公共模块
minSize: 0, // 抽取的chunk最小大小字节
minChunks: 2, // 最小引用数
reuseExistingChunk: true, // 当前 chunk 包含已从主 bundle 中拆分出的模块,则它将被重用
name(module, chunks, cacheGroupKey) { // 分离出模块文件名
const allChunksNames = chunks.map((item) => item.name).join('~') // 模块名1~模块名2
return `./js/${allChunksNames}` // 输出到 dist 目录下位置
}
}
}
}
},
// 解析
resolve: {
// 别名
alias: {
'@': path.resolve(__dirname, 'src')
}
}
}
// 开发环境下使用 sourcemap 选项
if (process.env.NODE_ENV === 'development') {
config.devtool = 'inline-source-map'
}
// 生产环境下使用相关配置
if (process.env.NODE_ENV === 'production') {
// 外部扩展(让 webpack 防止 import 的包被打包进来)
config.externals = {
// key:import from 语句后面的字符串
// value:留在原地的全局变量(最好和 cdn 在全局暴露的变量一致)
'bootstrap/dist/css/bootstrap.min.css': 'bootstrap',
'axios': 'axios',
'form-serialize': 'serialize',
'@wangeditor/editor': 'wangEditor'
}
}
module.exports = config