一、搭建框架
1.1 创建npm包管理
// 此命令可以搭建一个简易的npm包管理,并生成package.json文件,用来对依赖包进行管理
npm init -y
1.2 安装webpack
npm i webpack webpack-cli -D
- 安装后会生成node_module文件,后续所有的依赖包都会保存在该文件中
1.3 创建目录结构
1.4 编写测试代码
1.4 webpack打包
// npx命令 会自动将 node_module/bin 下作为环境变量
// 使用 webpack 对 main.js 为入口的文件进行打包
// --mode 为指定打包的模式 [development/production]
// development开发模式下,不会对代码进行压缩
// production生产模式下,会对代码进行压缩
// 打包完成后,会在根目录下生成dist目录
npx webpack ./src/main.js --mode=development
- 打包效果
- development模式
- npx webpack ./src/main.js --mode=development
- production模式
- npx webpack ./src/main.js --mode=production
1.5 查看页面效果
- 引入打包的资源
- vscode安装第三方插件 Live Server,用来打开页面
- 在index.html文件中右键打开
- 页面效果
1.6 使用webpack配置文件来配置打包
- 在项目根目录下创建 webpack.config.js 文件,来对打包进行相关配置
- webpack配置的基本结构
// 引入nodeJs的path模块
const path = require("path");
module.exports = {
// 配置 项目 入口
entry: "./src/main.js",
// 配置 打包文件 保存的位置
output: {
// 打包的绝对路径 path.resolve 会返回一个绝对路径
path: path.resolve(__dirname, "dist"),
},
// 配置 loader 转换器
// webpack 只能理解 JavaScript 和 JSON 文件.
// loader 让 webpack 能够去处理其他类型的文件,并将它们转换为有效 模块,以供应用程序使用,以及被添加到依赖图中。
module: {
rules: [],
},
// 配置插件
// loader 用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量。
plugins: [],
// 模式 【development、production】
mode: "development",
};
- 当我们配置好webpack.config.js后,就可以使用
npx webpack命令,该命令会自动使用webpack.config.js的配置进行打包
二、webpack基础配置
webpack只能识别js和json文件,对于其他文件(css、图片等)无法解析,但我们可以通过配置来转换这些文件,变成webpack能识别的文件
2.1 处理css资源
- 当我们创建css并引入后,未进行配置时,打包会报错
- 从报错信息中,我们可以看出,webpack无法识别css文件
- 我们可以查阅官网,来转换css
- 首先,你需要先安装
css-loader和style-loader
npm install --save-dev css-loader style-loader
- 配置loader
module: {
rules: [
{
// 匹配 .css 结尾的文件
test: /\.css$/i,
use: [
// use使用时,是从右到左,从下到上解析
// 即先使用 css-loader 将css解析为commonJs到js文件中,
// 在使用 style-loader 创建 style 标签将css引入到index.html 文件中
"style-loader",
"css-loader",
],
},
],
},
- 可以看到页面中创建了style标签引入了css资源
2.2 处理less资源
- 安装依赖
npm install less less-loader --save-dev
- 配置
module: {
rules: [
{
// 匹配 .less 结尾的文件
test: /\.less$/i,
use: [
"style-loader",
"css-loader",
// 将 less 转换为 css
"less-loader",
],
},
],
},
2.3 处理sass/scss资源
- 安装依赖
npm install sass-loader sass webpack --save-dev
- 配置
module: {
rules: [
{
test: /\.s[ac]ss$/i,
use: [
"style-loader",
"css-loader",
// 将 Sass/Scss 编译成 CSS
"sass-loader",
],
},
],
},
2.4 处理图片资源
在webpack5中,已经内置了图片资源的处理,即我们引入图片资源时,可以直接打包,但这种打包只是将图片资源直接拷贝到dist目录下,没有对图片进行任何处理;当存在较多小图片时,每次加载页面都请求,会增加服务器的压力,此时我们可以对小图片进行base64转换,减少http请求。
- 配置
module: {
rules: [
{
test: /\.(png|jpe?g|gif|svg|webp)$/,
type: "asset",
parser: {
dataUrlCondition: {
// 文件大小 小于10kb的,将打包成 base64 的格式,大于的将直接进行文件拷贝
// 优点:减少http请求 缺点:打包文件体积变大
maxSize: 10 * 1024, // 10kb
},
},
},
],
},
-
引入图片资源,200.png大小为3kb,dog1.png大小为30kb
-
可以看到,通过webpack配置,我们将图片资源打包到dist/static目录下,且目前只打包了一个图片资源
- 200.png已经转换为了base64格式
- dog1.png仍然为静态图片资源
2.5 修改文件打包输出目录
从之前的打包我们可以看出,不管是js文件还是图片,都直接放到dist目录下,显得有点杂乱,我们可以通过配置,来实现js文件存放在js目录下,图片存放在图片目录下
- 配置
- chunkFilename 的使用
// /* webpackChunkName: "math" */ webpack魔法命名
// 这里写文件名称,然后通过chunkFilename配置进行命名
import(/* webpackChunkName: "math" */ "./js/math").then(()=>{})
module.exports = {
output: {
// 打包的绝对路径 path.resolve 会返回一个绝对路径
path: path.resolve(__dirname, "dist"),
// js文件打包路径
// name 为原文件名称
// 入口文件打包名称
filename: "js/[name].js",
// 默认的静态资源存放路径
assetModuleFilename: "assets/[hash][ext][query]",
// 给打包输出的其他文件命名(动态导入的js文件、非入口js文件)
chunkFilename:'js/[name].js'
},
module: {
rules: [
{
test: /\.(png|jpe?g|gif|svg|webp)$/,
type: "asset",
parser: {
dataUrlCondition: {
// 文件大小 小于10kb的,将打包成 base64 的格式,大于的将直接进行文件拷贝
// 优点:减少http请求 缺点:打包文件体积变大
maxSize: 10 * 1024, // 10kb
},
},
generator: {
// 将资源打包到指定的目录中,若没有配置,则会使用上面的静态资源路径
// hash 为文件名哈希
// [hash:10] 为hash取前10位
// ext 为原文件的后缀
// query 为可选参数 如 url?xxxx
filename: "static/[hash][ext][query]",
},
},
],
},
};
- 可以看到js文件已经存放在js目录下,图片则存放在static目录下,这样阅读起来更舒服
2.6 打包时自动清空上一次打包文件
在还没有进行对应配置前,webpack每次打包都不会自动清除上一次打包的文件,需要我们手动进行清除,很不方便,此时我们可以通过clean配置自动清除
- 配置
// 当然,webpack5 以下,需要装插件实现
output: {
// 打包的绝对路径 path.resolve 会返回一个绝对路径
path: path.resolve(__dirname, "dist"),
// js文件打包路径
// name 为原文件名称
filename: "js/[name].js",
// 默认的静态资源存放路径
assetModuleFilename: "assets/[hash][ext][query]",
// 原理:打包前将 path 目录删除,然后再进行打包
clean: true,
},
2.7 处理字体图标(阿里巴巴矢量图)
- 下载矢量图 阿里巴巴矢量图
- 追加到购物车
- 下载到本地
- 将下载后的矢量图解压到项目中
- 可以通过demo.html查看具体的用法
- 项目中使用
- 将解压的文件解压到style/font目录下
- main.js引用iconfont.css
- 在index.html中使用icon
- 格式:
- 因为设置了默认资源打包路径,所以icon图标被打包到assets目录下
- 因为前面配置了js文件打包的目录,所以需要更改main.js的引用路径
- 页面效果
- 我们也可以单独配置font资源的打包目录
2.8 关于loader中的type属性值的解释
- type有几种属性
- asset/resource
- 将资源文件作为独立的文件输出,返回其URL。
- 比如引用图片路径
- 常用来处理图片、视频、音频资源等
- asset/inline
- 将资源文件作为DataURL内联到JavaScript中。
- 比如base64
- asset/source
- 将资源文件作为字符串导出,并返回其内容。
- 比如引入txt文本的内容
- asset
- 通用资源类型
- 现在,webpack 将按照默认条件,自动地在 resource 和 inline 之间进行选择:小于 8kb 的文件,将会视为 inline 模块类型,否则会被视为 resource 模块类型。
- 比如上文提到的处理图片资源
- asset/resource
2.9 处理Eslint
2.9.1 了解Eslint
- Eslint是用来检测js和jsx语法的工具,可以配置各项功能
- 我们使用Eslint,关键是写Eslint配置文件,里面写上各种rules规则,来运行Eslint对代码进行检测
- 配置文件
- 配置文件有多种写法
- .eslintrc.*:新建文件,位于项目的根目录
- .eslintrc
- .eslintrc.js:使用commonJS写法,可以写注释
- .eslintrc.json:使用json写法
- 区别在于配置的格式不一样
- 或者直接在package.json中的eslintConfig中直接写,这样就不需要创建文件(不推荐)
- 具体配置模板
// .eslintrc.js
module.exports = {
env: {
// 启用node中的全局变量
node: true,
// 启用浏览器中的全局变量
browser: true,
},
// 解析选项
parserOptions: {
// 配置 ES 语法版本
ecmaVersion: 6,
// ES 模块化
sourceType: "module",
// ES 其他特性
ecmaFeatures: {
jsx: true, // 如果是 React 项目,就需要开启 jsx 语法
},
},
// 具体规则
rules: {
// 禁止使用分号
semi: "error",
// 强制数组方法的回调函数中有return预计,否则告警
"array-callback-return": "warn",
"default-case": [
// 要求 switch 语句中有 default 分支,否则告警
"warn",
// 运行在最后注释 no default,就不会有告警了
{ commentPattern: "^no defalut$" },
],
eqeqeq: [
// 强制使用 === 和 !===,否则告警
"warn",
// 表示强制使用 ===和!==,但有些情况除外,
// 具体情况查看:https://eslint.nodejs.cn/docs/latest/rules/eqeqeq#smart
"smart",
],
// 禁止使用var
"no-var": 2,
},
// 继承其他规则
// 继承eslint默认规则
extends: ["eslint:recommended"],
// 其他规则详见:
// 英文官网:https://eslint.org/docs/latest/rules/
// 中文官网:https://eslint.nodejs.cn/docs/latest/rules/
};
- rules 具体规则
off或0:关闭规则warn或1:开启规则,使用警告级别的错误(不会导致程序退出,仅作告警)error或2:开启规则,使用错误级别的错误(当被触发时,程序会直接中断)
2.9.2 使用Eslint
- 安装依赖包
npm i eslint-webpack-plugin eslint -D
- 配置.eslintrc.js文件(使用上面的模板)
- webpack配置eslint
// webpack.config.js
// 引入Eslint
const ESLintPlugin = require("eslint-webpack-plugin");
// plugins配置中添加eslint
plugins: [
new ESLintPlugin({
// 配置哪些文件要进行检查
context: path.resolve(__dirname, "src"),
}),
],
- 安装eslint提示插件
- 查看效果
- 这些爆红是eslint插件的提示,但是我们打包的dist目录下的js也爆红了
- 若我们想排除某些文件的告警,可以通过如下配置
- 在根目录创建
.eslintignore文件(类似于git的忽略文件.gitignore) - 在其中配置忽略的文件/目录
- 可以看到,dist下的main.js已经不爆红了
- 在根目录创建
2.10 处理Babel
2.10.1 了解Babel
- 主要用于将ES6语法编写的代码转换为向后兼容的JavaScript语法,以便能够运行在当前和旧版本的浏览器或其他环境中
- 配置文件有多种写法
- babel.config.*:新建文件,位于项目根目录
- babel.config.js
- babel.config.json
- .babelrc.*:新建文件,位于项目根目录
- .babelrc
- .babelrc.js
- .babelrc.json
- package.json中的babel中
- babel.config.*:新建文件,位于项目根目录
2.10.2 配置babel
- 安装依赖
npm install -D babel-loader @babel/core @babel/preset-env webpack
- 配置webpack.config.js
// webpack.config.js
module: {
rules: [
// 配置babel转换器
{
test: /\.js$/,
// 排除node_modules目录
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ["@babel/preset-env"],
},
},
},
],
},
- 配置babel规则
// babel.config.js
module.exports = {
// 智能预设,能够编译 ES6
presets: ["@babel/preset-env"],
};
- 还没有配置babel之前,可以看到es6的语法打包之后,仍然还是es6语法(箭头函数)
- 配置了babel后,已经不是箭头函数了
2.11 处理html资源
之前我们打包后,需要手动引入 main.js 的入口文件,当我们改变了打包输出结构的话,又需要手动去更改,非常的不方便,因此我们希望可以实现自动引入(html-webpack-plugin)
- 安装依赖包
npm install --save-dev html-webpack-plugin
- 配置 webpack.config.js
// webpack.config.js
// 引入 html处理插件
const HtmlWebpackPlugin = require("html-webpack-plugin");
plugins: [
// 配置 html自动引入功能
new HtmlWebpackPlugin(),
],
- 效果:我们可以看到打包后自动在dist目录下创建了index.html文件,且引入了main.js文件
- 但丢失了我们之前写的配置
- 可以通过该插件配置生成的html模板,这样就不会丢失我们写好的配置
plugins: [
// 配置 html自动引入功能
new HtmlWebpackPlugin({
// 配置模板文件
template: path.resolve(__dirname, "public/index.html"),
}),
],
- 效果
2.12 热更新(开发服务器)
在之前的操作中,我们每次对文件修改后,都需要重新打包才能看到效果,非常的鸡肋,因此我们可以通过配置热更新 webpack-dev-server ,实现代码发生变动,自动重新编译,即无需打包就可以看到效果变化
- 安装依赖
npm install --save-dev webpack-dev-server
- 配置webpack.config.js
module.export = {
devServer: {
// 配置启动服务器的域名
host: "localhost",
// 配置服务端口
port: "8080",
// 是否自动打开浏览器
open: true,
},
}
- 启动服务器:npx webpack server
- 运行后,会自动打开浏览器,而且我们修改了文件内容,会自动重新编译
- 这种运行方式,是在内存中打包的,故dist文件不会发生变化
2.13 配置开发环境和生产环境
针对开发环境和生产环境的不同需求,需要针对进行不同的配置,故可以抽离配置
- 调整目录
- 新增config目录,创建生产环境和开发环境的配置文件
- 开发环境配置
- 注意
- 因为运行的时候是在根目录下运行
- 故相对路径无需更改
- 但绝对路径需要做调整
- 注意
// webpack.dev.js
const path = require("path");
// 引入Eslint
const ESLintPlugin = require("eslint-webpack-plugin");
// 引入 html处理插件
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
entry: "./src/main.js",
output: {
// 打包的绝对路径 path.resolve 会返回一个绝对路径
// path: path.resolve(__dirname, "../dist"),
path: undefined, // 开发环境不需要打包
// js文件打包路径
// name 为原文件名称
filename: "js/[name].js",
// 默认的静态资源存放路径
assetModuleFilename: "assets/[hash][ext][query]",
// 原理:打包前将 path 目录删除,然后再进行打包
// clean: true,
},
module: {
rules: [
{
// 匹配 .css 结尾的文件
test: /\.css$/i,
use: [
// use使用时,是从右到左,从下到上解析
// 即先使用 css-loader 将css解析为commonJs到js文件中,
// 在使用 style-loader 创建 style 标签将css引入到index.html 文件中
"style-loader",
"css-loader",
],
},
{
// 匹配 .less 结尾的文件
test: /\.less$/i,
use: [
"style-loader",
"css-loader",
// 将 less 转换为 css
"less-loader",
],
},
{
test: /\.s[ac]ss$/i,
use: [
"style-loader",
"css-loader",
// 将 Sass/Scss 编译成 CSS
"sass-loader",
],
},
{
test: /\.(png|jpe?g|gif|svg|webp)$/,
type: "asset",
parser: {
dataUrlCondition: {
// 文件大小 小于10kb的,将打包成 base64 的格式,大于的将直接进行文件拷贝
// 优点:减少http请求 缺点:打包文件体积变大
maxSize: 10 * 1024, // 10kb
},
},
generator: {
// 将资源打包到指定的目录中,若没有配置,则会使用上面的静态资源路径
// hash 为文件名哈希
// [hash:10] 为hash取前10位
// ext 为原文件的后缀
// query 为可选参数 如 url?xxxx
filename: "static/[hash][ext][query]",
},
},
{
test: /\.(ttf|woff2?)$/,
type: "asset/resource",
generator: {
// 将资源打包到指定的目录中,若没有配置,则会使用上面的静态资源路径
filename: "font/[hash][ext][query]",
},
},
{
test: /\.js$/,
// 排除node_modules目录
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ["@babel/preset-env"],
},
},
},
],
},
plugins: [
new ESLintPlugin({
// 配置哪些文件要进行检查
context: path.resolve(__dirname, "../src"),
}),
// 配置 html自动引入功能
new HtmlWebpackPlugin({
// 配置模板文件
template: path.resolve(__dirname, "../public/index.html"),
}),
],
// 模式 【development、production】
mode: "development",
devServer: {
// 配置启动服务器的域名
host: "localhost",
// 配置服务端口
port: "8080",
// 是否自动打开浏览器
open: true,
},
};
- 生产环境配置
- 注意
- 因为运行的时候是在根目录下运行
- 故相对路径无需更改
- 但绝对路径需要做调整
- 注意
// webpack.prod.js
const path = require("path");
// 引入Eslint
const ESLintPlugin = require("eslint-webpack-plugin");
// 引入 html处理插件
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
entry: "./src/main.js",
output: {
// 打包的绝对路径 path.resolve 会返回一个绝对路径
path: path.resolve(__dirname, "../dist"),
// js文件打包路径
// name 为原文件名称
filename: "js/[name].js",
// 默认的静态资源存放路径
assetModuleFilename: "assets/[hash][ext][query]",
// 原理:打包前将 path 目录删除,然后再进行打包
clean: true,
},
module: {
rules: [
{
// 匹配 .css 结尾的文件
test: /\.css$/i,
use: [
// use使用时,是从右到左,从下到上解析
// 即先使用 css-loader 将css解析为commonJs到js文件中,
// 在使用 style-loader 创建 style 标签将css引入到index.html 文件中
"style-loader",
"css-loader",
],
},
{
// 匹配 .less 结尾的文件
test: /\.less$/i,
use: [
"style-loader",
"css-loader",
// 将 less 转换为 css
"less-loader",
],
},
{
test: /\.s[ac]ss$/i,
use: [
"style-loader",
"css-loader",
// 将 Sass/Scss 编译成 CSS
"sass-loader",
],
},
{
test: /\.(png|jpe?g|gif|svg|webp)$/,
type: "asset",
parser: {
dataUrlCondition: {
// 文件大小 小于10kb的,将打包成 base64 的格式,大于的将直接进行文件拷贝
// 优点:减少http请求 缺点:打包文件体积变大
maxSize: 10 * 1024, // 10kb
},
},
generator: {
// 将资源打包到指定的目录中,若没有配置,则会使用上面的静态资源路径
// hash 为文件名哈希
// [hash:10] 为hash取前10位
// ext 为原文件的后缀
// query 为可选参数 如 url?xxxx
filename: "static/[hash][ext][query]",
},
},
{
test: /\.(ttf|woff2?)$/,
type: "asset/resource",
generator: {
// 将资源打包到指定的目录中,若没有配置,则会使用上面的静态资源路径
filename: "font/[hash][ext][query]",
},
},
{
test: /\.js$/,
// 排除node_modules目录
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ["@babel/preset-env"],
},
},
},
],
},
plugins: [
new ESLintPlugin({
// 配置哪些文件要进行检查
context: path.resolve(__dirname, "../src"),
}),
// 配置 html自动引入功能
new HtmlWebpackPlugin({
// 配置模板文件
template: path.resolve(__dirname, "../public/index.html"),
}),
],
// 模式 【development、production】
mode: "production",
// 生产环境不需要开发服务器调试
// devServer: {
// // 配置启动服务器的域名
// host: "localhost",
// // 配置服务端口
// port: "8080",
// // 是否自动打开浏览器
// open: true,
// },
};
- 运行
- 开发环境
- npx webpack serve --config .\config\webpack.dev.js
- 生产环境
- npx webpack --config .\config\webpack.prod.js
- 开发环境
- 每次运行都需要写这么长的命令,容易写错,故我们可以给命令添加快捷键
// package.json
{
"name": "webpack-code",
"version": "1.0.0",
"description": "",
"main": "/src/main.js",
"scripts": {
"start": "npm run dev",
"dev": "webpack serve --config ./config/webpack.dev.js",
"build": "webpack --config ./config/webpack.prod.js ",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.22.1",
"@babel/preset-env": "^7.22.2",
"babel-loader": "^9.1.2",
"css-loader": "^6.7.4",
"eslint": "^8.41.0",
"eslint-webpack-plugin": "^4.0.1",
"html-webpack-plugin": "^5.5.1",
"less": "^4.1.3",
"less-loader": "^11.1.0",
"sass": "^1.62.1",
"sass-loader": "^13.3.0",
"style-loader": "^3.3.3",
"webpack": "^5.84.1",
"webpack-cli": "^5.1.1",
"webpack-dev-server": "^4.15.0"
}
}
2.14 处理css
2.14.1 抽取css为单独文件
- 安装依赖
npm install --save-dev mini-css-extract-plugin
- 配置webpack.config.js
// webpack.config.js
// 引入 css 处理模块,可以将css提取到单独的文件中
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
plugins: [
// css 抽取模块
new MiniCssExtractPlugin({
filename: "css/[hash].css",
}),
],
module: {
rules: [
{
// 匹配 .css 结尾的文件
test: /\.css$/i,
use: [
// use使用时,是从右到左,从下到上解析
// 即先使用 css-loader 将css解析为commonJs到js文件中,
// 在使用 style-loader 创建 style 标签将css引入到index.html 文件中
MiniCssExtractPlugin.loader,
"css-loader",
],
},
{
// 匹配 .less 结尾的文件
test: /\.less$/i,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
// 将 less 转换为 css
"less-loader",
],
},
{
test: /\.s[ac]ss$/i,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
// 将 Sass/Scss 编译成 CSS
"sass-loader",
],
},
],
},
- 效果
2.14.2 css兼容处理
- 安装依赖
npm install --save-dev postcss-loader postcss postcss-preset-env
- 配置webpack.config.js
// 定义公用方法
const getStyleLoader = (pre) => {
return [
MiniCssExtractPlugin.loader,
"css-loader",
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
[
"postcss-preset-env",
{
// 其他选项
},
],
],
},
},
},
pre,
].filter(Boolean);
};
module: {
rules: [
{
// 匹配 .css 结尾的文件
test: /\.css$/i,
use: getStyleLoader(),
},
{
// 匹配 .less 结尾的文件
test: /\.less$/i,
use: getStyleLoader("less-loader"),
},
{
test: /\.s[ac]ss$/i,
use: getStyleLoader("sass-loader"),
},
],
},
- 配置package.json
"browserslist": [
"ie >= 8"
]
// 通常配置为
"browserslist": [
"last 2 version",
"> 1%",
"not dead"
]
- 效果
2.15 css压缩
- 安装依赖
npm install css-minimizer-webpack-plugin --save-dev
- 配置webpack.config.js
// 引入 css 代码压缩插件
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
plugins: [
new CssMinimizerPlugin(),
],
三、webpack高级配置
3.1 提高开发体验
3.1.1 错误调试 sourceMap
- SourceMap的值有很多种,但我们一般关心两种
- cheap-module-source-map
- 优点:打包编译速度快,只包含行映射
- 缺点:没有列映射
- 即我们查看报错信息时,只知道是哪一行报错了,但不知道是哪一列
- source-map
- 优点:包含行/列映射
- 缺点:打包编译速度更慢
- cheap-module-source-map
- 一般情况下,我们只在开发环境配置,生产环境一般不允许配置文件映射,可能会被别人反编译找代码漏洞
- 具体配置
devtool: "source-map",
- 可以看到每个文件都有对应的map映射文件
3.1.2 HMR(热替换HotModuleReplacementPlugin)
当某个文件被修改了,只会重新编译这个文件,并替换掉,即页面不会刷新
- 不适合生产模式
- 实际上webpack默认启用了HMR功能
- css使用 style-loader时,里面默认支持HMR功能
- js默认不支持HMR,需要手动去配置
- 若使用vue项目
- 可以使用vue-loader,内部已经集成了HMR
// webpack.config.js
devServer:{
hot:true, // 是否开启HMR功能 默认为true
}
- js配置
// main.js
if(module.hot){
// 判断是否支持热模块替换功能
module.hot.appect('./js/count');
module.hot.appect('./js/sum',function(){})
}
3.2 提高打包构建速度
3.2.1 rule.oneOf
- 在webpack.config.js中的loader处理规则中,每个文件都是从第一个规则开始匹配,当匹配到对应的规则后,仍然会继续向下匹配,当文件变多的时候,会在一定程度上延长构建的速度
- 此时我们可以通过
oneOf让每个文件只接受一次loader处理
// webpack.config.js
module.export = {
module:{
rules:[
{
// 以下的规则匹配每个文件只会触发一次
oneOf:[
{
// 匹配 .css 结尾的文件
test: /\.css$/i,
use: getStyleLoader(),
},
{
// 匹配 .less 结尾的文件
test: /\.less$/i,
use: getStyleLoader("less-loader"),
},
{
test: /\.s[ac]ss$/i,
use: getStyleLoader("sass-loader"),
},
]
}
]
}
}
3.2.2 include/exclude(包含/排除)
- loader、plugins等都可以通过
include和exclude对文件进行包含/排除- 但这两个在同一个地方只能存在一个,若同时存在会报错
module.export = {
module:{
rules:{
{
test: /\.js$/,
// 排除node_modules目录
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ["@babel/preset-env"],
},
},
},
}
},
plugins:[
new ESLintPlugin({
// 配置哪些文件要进行检查
context: path.resolve(__dirname, "../src"),
include:path.resolve(__dirname,'../src')
}),
]
}
3.2.3 Eslit和Babel的缓存功能
- 构建项目的时候,绝大部分时间花在Eslint代码检查和Babel代码兼容转换过程中
- 我们可以为Eslint和Babel开启缓存
- 即第一次构建速度不变
- 但第二次及之后的构建因为有缓存,所以一定程度上提高了
3.2.4 Eslint、Babel、Terser多进程打包处理
-
提升打包速度,其实就是提升js的打包速度,因为其他文件都比较少
-
而对js文件处理的主要就是eslint、babel、terser三个工具,所以我们要提升他们的运行速度
-
可以开启多进程同时处理js文件
-
安装依赖
npm i thread-loader -D
- 配置
// 引入node模块
const os = require('os')
// cpu核数
const threads = os.cpus().length;
// 这个插件默认已经安装了,直接引入就好
const TerserWebpackPlugin = require('terser-webpack-plugin');
module: {
rules: [
// 配置babel转换器
{
test: /\.js$/,
// 排除node_modules目录
exclude: /node_modules/,
use: [
{
// 开启多进程
loader:'thread-loader',
options:{
// 进程数量
works:threads,
}
},
{
loader: "babel-loader",
options: {
// presets: ["@babel/preset-env"],
// 开启babel缓存
cacheDirectory:true,
// 关闭缓存文件压缩
cacheCompression:false,
},
},
]
},
],
},
// plugins配置中添加eslint
plugins: [
new ESLintPlugin({
// 配置哪些文件要进行检查
context: path.resolve(__dirname, "src"),
exclude:'node_modules', // 默认值
cache:true, // 开启缓存
// 配置eslint缓存文件地址
cacheLocation:path.resolve(__dirname,'../node_modules/.cache/eslintcache'),
threads, // 进程数
}),
// 压缩js,这个配置默认就有了,只是要配置进程数,所以重新写配置
new TerserWebpackPlugin({
parallel:threads, // 开启多进程和设置进程数量
})
],
3.3 减少代码体积
3.3.1 Tree Shaking
- 可以用于移除javaScript中没有使用上的代码
- 如 一个js文件中定义了多个方法,但main.js中只引用并使用了其中一个方法,则打包时仅打包编译那个引用的方法,其他方法不打包编译
- 注意:它依赖于
ES Module - 故在commonJs中无法使用
- webpack默认已经支持并开启了该功能
3.3.2 Babel
- Babel为编译的每个文件都插入了辅助代码,使代码体积过大
- Babel对一些公用方法使用了非常小的辅助代码,比如_extend。默认情况下会被添加到每一个需要它的文件中(即重复引入)
- 我们可以将这些辅助代码作为一个独立模块,来避免重复引入
- @babel/plugin-transform-runtime
- 禁用了Babel自动对每个文件的runtime注入,而是引入
- 并且使所有辅助代码从这里引用
- 安装依赖
npm i @babel/plugin-transform-runtime -D
- 配置
module: {
rules: [
// 配置babel转换器
{
test: /\.js$/,
// 排除node_modules目录
exclude: /node_modules/,
use: [
{
// 开启多进程
loader:'thread-loader',
options:{
// 进程数量
works:threads,
}
},
{
loader: "babel-loader",
options: {
// presets: ["@babel/preset-env"],
// 开启babel缓存
cacheDirectory:true,
// 关闭缓存文件压缩
cacheCompression:false,
plugins:['@babel/plugin-transform-runtime'], // 减少代码体积
},
},
]
},
],
},
3.3.3 图片压缩
3.4 优化代码运行性能
3.4.1 Code Split
- 打包代码时会将所有的js文件打包到一个文件中,体积太大了,如果只需要渲染首页,就应该只加载首页的js文件,其他文件不应该加载
- 所以我们需要将打包生成的文件进行代码分割,生成多个js文件,渲染那个页面就加载哪个js文件,这样加载的资源就越少,速度就更快
- 代码分割:
- 分割文件:将打包生成的文件进行分割,生成多个js文件
- 按需加载:需要哪个文件就加载哪个文件
3.4.1.1 多入口
- 创建一个测试项目
- 安装依赖包
npm i html-webpack-plugin webpack webpack-cli -D
3.4.1.2 多入口提取公用模块
- 当存在多个入口文件时
- 多个入口文件同时引用一份公用代码
- 会导致该代码每个入口文件都打包一次
- 我们可以通过配置将公用部分进行抽取,减少代码体积
- 通过webpack.config.js中的
optimization.splitChunks进行配置
- 通过webpack.config.js中的
3.4.2 preload/prefetch 预加载资源
- vue也有对应的
preload/prefetch插件- @vue/preload-webpack-plugin
- www.npmjs.com/package/@vu…
3.4.3 文件映射提取
- 在没有配置之前,
- 我们在a文件引用b文件的方法
- 当b文件发生变动,a文件没有发生变动
- 当我们打包时,如果文件名是用的hash或其他可变动的名称来命名
- 由于a文件中引用了b文件,当b文件名变化后,a文件的引入也会变
- 故打包时,a文件和b文件都重新编译并打包,会影响构建编译速度
- 解决方案
- 将文件引用映射抽取出来,这样每次修改b文件时,只会重新构建映射文件和修改的b文件
- 配置
3.4.4 core-js解决js兼容性问题
- 安装依赖
- 前提是配置了babel
- 按需自动导入
npm i core-js -D
npm install -D babel-loader @babel/core @babel/preset-env
- 配置
// webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
entry: {
app: "./src/app.js",
main: "./src/main.js",
},
output: {
path: path.resolve(__dirname, "./dist"),
filename: "[name].js",
clean: true,
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "./public/index.html"),
}),
],
optimization: {
// 配置分包,将node_module分包打包
splitChunks: {
chunks: "all",
},
},
module: {
rules: [
// 使用babel
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
},
},
],
},
mode: "production",
};
// babel.config.js
module.exports = {
presets: [
[
"@babel/preset-env",
{
// usage 会根据配置的浏览器兼容,以及你代码中用到的 API 来进行 polyfill,实现了按需添加。
useBuiltIns: "usage",
// 指定用的corejs版本
corejs: 3,
},
],
],
};
- 效果
3.4.5 PWA技术(网络离线访问页面)
开发web App项目,项目一旦处于网络离线的情况,就没法访问了,此时我们希望项目提供离线访问体验
- 渐进式网络应用程序(
progressive web application - PWA)- 是一种可以提供类似于native app(原生应用程序)体验的web App技术
- 其中最重要的是在
离线时应用程序仍能够继续运行 - 内部通过
Service Workers技术实现 - webpack.docschina.org/guides/prog…
- 安装依赖
npm install workbox-webpack-plugin --save-dev
- 配置
// webpack.config.js
const WorkboxPlugin = require('workbox-webpack-plugin');
plugins: [
// 使用插件
new WorkboxPlugin.GenerateSW({
// 这些选项帮助快速启用 ServiceWorkers
// 不允许遗留任何“旧的” ServiceWorkers
clientsClaim: true,
skipWaiting: true,
}),
],
- 注册Service Workers
// main.js
// 注册 service works
if ("serviceWorker" in navigator) {
window.addEventListener("load", () => {
navigator.serviceWorker
.register("/service-worker.js")
.then((registration) => {
console.log("SW registered: ", registration);
})
.catch((registrationError) => {
console.log("SW registration failed: ", registrationError);
});
});
}
- 查看
- 需要部署服务端环境
- 若没有部署环境
- 可以使用
http-server插件 npm i http-server -g安装插件- 在dist目录下运行
http-server
- 可以使用
- 第一次进入后,此时把网络断开,仍然能访问