webpack5 入门学习笔记(四)性能优化
写在前面
webpack优化配置
开发环境性能优化
- 优化打包构建速度
HMR模块热替换- 优化代码调试
source-map生产环境性能优化
- 优化打包构建速度
oneOfbabel 缓存多进程打包同一时间干多件事,提升打包速度,进程开启和交流也有开销,消耗时间比较长的任务,使用 thread-loader 对 babel-loader 进行优化external声明不用打包的库,再用cdn引进来- 优化代码运行的性能
缓存(hash-chunkhash-contenthash)tree shaking(es6 模块,production)(package.json 中 sideEffects:[‘xxx’])code split{单页面,单入口通过 import 引入的一定会被分割成单独的打包,optimization}懒加载/预加载
- 代码分割是异步执行,使用懒加载
- 预加载等其他资源加载完再加载,预加载兼容性问题大用之慎重
pwa离线可访问,serviceworker 和 cache,使网络离线可访问,兼容性问题严重
HMR
定义
- hot module replacement 热模块替换 / 模块热替换
作用
- 一个模块发生变化,只会重新打包这一个模块(而不是打包所有模块),极大提升构建速度
使用方法
devServer中加上hot:truedevServer: { hot:true }- 样式文件:可以使用
HMR功能 ,因为style-loader内部实现了,所以开发环境下用style-loader,但是生产环境下还是提取css为单独文件,就不能使用了loader:'style-loader' - html文件: 默认不能使用
HMR功能,开启HMR同时会导致- 问题:
html文件不能热更新了 - 解决:修改
entry入口,将html文件引入(html文件只有一个,不用做HMR功能)
//入口文件 entry:['./src/js/index.js','./src/index.html'], - 问题:
- js文件:默认不能使用
HMR功能 -->需要修改js代码,添加支持HMR功能的代码- 注意:
HMR功能对js的处理,只能处理非入口js文件的其他文件 - 在
index.js文件中加入以下代码
if (module.hot) { //全局中寻找hot //一旦module.hot为true,说明开启了HMR功能。 -->让HMR功能代码生效 module.hot.accept('./print.js', function () { //方法会监听print.js文件的变化,一旦发生变化,其他默认不会重新打包构建 //会执行后面的回调函数 print() }) } - 注意:
要点记录
- 当修改了
webpack配置,新配置要想生效必须重启webpack服务
代码实现
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
//入口文件
entry:['./src/js/index.js','./src/index.html'],
//出口
output: {
//输出的文件名
filename: 'js/bundle.js',
//输出的路径
path: resolve(__dirname, 'dist'),
clean:true //清理旧文件
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.less$/,
use: ['style-loader', 'css-loader','less-loader']
},
{
//处理图片资源(样式中的图片资源,html中的是处理不了的)
test: /\.(jpg|png|gif)$/,
loader: 'url-loader', //通过es6module解析
options: {
limit: 8 * 1024,
name: '[name].[hash:10].[ext]',
//关闭es6模块化,开启commonjs模块化
esModule: false,
outputPath:'img'
}
},
{
//处理html中的img资源
test: /\.html$/,
loader: 'html-loader',
options: {
esModule: false
}
},
{
//处理其他资源
exclude: /\.(html|js|css|less|jpg|png|gif)$/,
loader: 'file-loader', //url-loader是基于file-loader封装的,多一些压缩功能,比如讲图片转成base64格式
options: {
name: '[name].[hash:10].[ext]',
outputPath:'media'
}
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
//inject: 'head',
scriptLoading: 'blocking',
title:'Hot Module Replacement'
}),
],
mode: 'development',
target: 'web',
devServer: {
contentBase: './dist',
//启动gzip压缩
compress: true,
//端口号
port: 8888,
//首次运行自动打开浏览器
open: 'Chrome',
// 开启HMR功能
//当修改了webpack配置,新配置要想生效必须重启webpack服务
hot: true
}
};
source-map
定义
source-map: 一种 提供源代码到构建后代码映射 技术 (如果构建后代码出错了,通过映射关系可以追踪源代码错误)
作用
- 一个模块发生变化,只会重新打包这一个模块(而不是打包所有模块),极大提升构建速度
使用方法
devtool:'source-map'基本配置devtool: 'source-map' //生产环境选择 devtool: 'eval-source-map' //开发环境选择- 几个可选参数:
[inline-|hidden|eval-][nosources-][cheap-[module-]]source-map - 内联和外部
- 外部生成了
map文件 - 内联在
bundle.js文件中 - 内联构建速度更快
- 外部生成了
source-map外部
- 错误代码的准确信息 和 源代码的错误位置
inline-source-map内联
- 只生成一个内联source-map
- 错误代码的准确信息 和 源代码的错误位置
hidden-source-map外部
- 错误代码错误原因,但是没有错误位置
- 不能追踪源代码错误,只能看到构建后代码的错误位置
eval-source-map内联
- 每个文件后面追加一个source-map 在eval函数中
- 错误代码的准确信息 和 源代码的错误位置,多一个hash值
nosources-source-map外部
- 错误代码的准确信息,但是没有任何源代码信息
cheap-source-map外部
- 错误代码的准确信息 和 源代码的错误位置
- 只能精确到行,不能精确到列
cheap-module-source-map外部
- 错误代码的准确信息 和 源代码的错误位置
- module会将loader的source map加入
- 开发环境:速度快,调试更友好
- 速度快(
eval>inline>cheap>...)eval-cheap-source-map第一eval-source-map- 调试友好
source-map第一cheap-module-source-mapcheap-source-map- 又快又好 – >
eval-source-map / eval-cheap-module-source-map
- 生产环境:源代码要不要隐藏? 调试要不要更友好
- 内联会让代码体积变大,所以在生产环境不用内联
- 源代码隐藏
nosources-source-map全部隐藏hidden-source-map只隐藏源代码,会提示构建后代码错误- – >
调试友好 source-map / 速度快 cheap-module-source-map
代码实现
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
//入口文件
entry:['./src/js/index.js','./src/index.html'],
//出口
output: {
//输出的文件名
filename: 'js/bundle.js',
//输出的路径
path: resolve(__dirname, 'dist'),
clean:true //清理旧文件
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.less$/,
use: ['style-loader', 'css-loader','less-loader']
},
{
//处理图片资源(样式中的图片资源,html中的是处理不了的)
test: /\.(jpg|png|gif)$/,
loader: 'url-loader', //通过es6module解析
options: {
limit: 8 * 1024,
name: '[name].[hash:10].[ext]',
//关闭es6模块化,开启commonjs模块化
esModule: false,
outputPath:'img'
}
},
{
//处理html中的img资源
test: /\.html$/,
loader: 'html-loader',
options: {
esModule: false
}
},
{
//处理其他资源
exclude: /\.(html|js|css|less|jpg|png|gif)$/,
loader: 'file-loader', //url-loader是基于file-loader封装的,多一些压缩功能,比如讲图片转成base64格式
options: {
name: '[name].[hash:10].[ext]',
outputPath:'media'
}
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
//inject: 'head',
scriptLoading: 'blocking'
}),
],
mode: 'development',
target: 'web',
devServer: {
contentBase: './dist',
//启动gzip压缩
compress: true,
//端口号
port: 8888,
//首次运行自动打开浏览器
open: 'Chrome',
// 开启HMR功能
//当修改了webpack配置,新配置要想生效必须重启webpack服务
hot: true
},
//devtool: 'source-map' //生产环境选择
devtool: 'eval-source-map' //开发环境选择
};
oneOf
定义
- 以下
loader只会匹配一个
作用
- 优化生产环境打包构建速度
使用方法
- 在众多
loader外嵌套一层oneOf: [ { //css兼容性处理 test: /\.css$/, use: [...commonCssloader], }, //... ]
要点记录
oneOf里不能有两个配置处理同一种类型文件
代码实现
/**
* oneOf:优化生产环境打包构建速度
*/
//绝对路径解析方法
const { resolve } = require("path");
//提取css成单独文件插件
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
//最新压缩css插件
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
//手动添加webpack内部js压缩器
const TerserWebpackPlugin = require('terser-webpack-plugin');
//对html文件处理
const HtmlWebpackPlugin = require('html-webpack-plugin');
//默认使用生产环境,定义nodejs环境变量:决定使用browserslist的哪个环境
process.env.NODE_ENV = 'production'
//复用loader
const commonCssloader = [
MiniCssExtractPlugin.loader,//提取css为单独文件
'css-loader',
{//兼容性处理 还需要在package.json中定义browserslist
loader: 'postcss-loader',
options: {
postcssOptions: {
ident: 'postcss',
plugins: [
'postcss-preset-env'
]
}
}
}
]
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/bundle.js',
path:resolve(__dirname,'dist'),
clean: true,
publicPath:'./'
},
//压缩css
optimization: {
minimize: true,
minimizer: [
new TerserWebpackPlugin(),
new CssMinimizerPlugin()
]
},
module: {
rules: [
/**
* 正常来讲,一个文件只能被一个loader处理
* 当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序
* 在loader中设置enforce:'pre',最先执行
*/
{
//优化生产环境打包构建速度
//以下loader只会匹配一个
//注意:这里不能有两个配置处理同一种类型文件
oneOf: [
{
//css兼容性处理
test: /\.css$/,
use: [...commonCssloader],
},
{
//less兼容性处理
test: /\.less$/,
use: [...commonCssloader,
//use中loader执行从下往上,必须把这个处理放在css-loader与less-loader之间
'less-loader'//将less转成css文件
],
},
{
//js兼容性处理
test: /\.js$/,
exclude: /node_modules/,
loader:'babel-loader' //配置在.babelrc文件中
},
{
//图片处理
test: /\.(jpg|png|gif)$/,
loader: 'url-loader',
options: {
limit: 8 * 1024,//对8kb以下的图片进行base64处理
name: '[name].[hash:8].[ext]',//对处理后的图片重命名
outputPath: 'img', //设置处理后的图片路径
publicPath: './', //指定打包后的css图片的基础路径,
esModule:false //关闭es6模块化
}
},
{
//处理html中的图片问题
test: /\.html$/,
loader: 'html-loader',
options: {
esModule:false
}
},
// 打包iconfont字体图标,和打包图片类似
{
test:/\.ttf$/,
use: {
loader: 'file-loader',
options: {
esModule: false, // 新版本中esModule默认为true,会导致文件的地址变为[object Module],因此这里设置为false
name: '[name]_[hash:6].[ext]', // 输出的文件名为[原名称]_[哈希值].[原后缀]
outputPath: 'fonts/', // 文件存储路径(output.path + 值)(物理路径, 存储路径)
publicPath:'../fonts' , // 负责输出目录, 即打包后的写在磁盘的位置
// 输出解析文件的目录,url 相对于 HTML 页面(index.html所在文件夹的绝对路径 + 值)(文件引用路径就是看这个)
// 如果output设置了publicPath, options也设置了publicPath,优先以options的publicPath为主
// 是对页面引入资源的补充,比如img标签引入或者css引入等.
// 千万不能设错,应该观察文件和HTML页面的存储地址位置,进行设置,否则引用时地址会错误,找不到文件
// 一般只设置output的publicPath,方便统一管理
limit: 1024 // 限制当文件小于1KB的时候,就将文件转为base64存储于js中,以减少http请求次数,当文件大于1KB,则打包文件到指定目录,避免js过大
}
}
},
{
//处理其他文件
exclude: /\.(js|css|less|html|jpg|png|gif|ttf)$/,
loader: 'file-loader',
options: {
name: '[name].[hash:8].[ext]',
outputPath:'media'
},
// type: 'asset/resource',
// generator: {
// filename: 'media/[name].[hash:6].[ext]'
// }
},
]
}
]
},
devtool: 'source-map',//错误追踪
plugins: [
//提取css为单独文件
new MiniCssExtractPlugin({
filename:'css/built.css'
}),
//对html文件处理
new HtmlWebpackPlugin({
template: './src/index.html',
scriptLoading: 'blocking',//去除script defer模式
//html压缩
mimify: {
//移除空格
collaspeWhitespace: true,
//移除注释
removeComments:true
}
})
],
//mode为production,js就自动压缩了
mode:'production'
}
缓存
作用
- babel缓存
- 让第二次打包构建速度更快
- 文件缓存
- 让代码上线运行缓存更好使用 上线代码性能优化
使用方法
- 开启
babel缓存{ //js兼容性处理 test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader', //配置在.babelrc文件中 options: { //开启babel缓存,第二次构建时会读取之前的缓存 //问题:当文件名没有发生变化的时候,同名文件都是走缓存。会导致修改内容与实际展示内容不一致。 cacheDirectory:true } } - 添加
contenthash值output: { filename: 'js/bundle.[contenthash:10].js', path:resolve(__dirname,'dist'), clean: true, publicPath:'./' }, plugins: [ //提取css为单独文件 new MiniCssExtractPlugin({ filename:'css/built.[contenthash:10].css' }), } - 创建临时服务器 新建server.js文件 启动指令 node server.js
/** * 服务器代码 * 启动指令 node server.js * 使用nodemon server.js 需要全局安装nodemon npm i nodemon -g */ const express = require('express') const app = express(); app.use(express.static('dist', { maxAge: 1000 * 3600 })); app.listen(3000); - 打包之后,启动服务器,在浏览器中查看,
network,刷新就能看到,后面是通过读取缓存内容显示的,速度也会更快
要点记录
- babel缓存
- 将
babel处理后的资源缓存起来(哪里的js改变就更新哪里,其他js还是用之前缓存的资源),让第二次打包构建速度更快 - 因为
js文件最多 编译过程 类似HMR功能,但是生产环境不能用HMR功能 - 所以在生产环境下 开启
babel缓存,第二次构建时,会读取之前的缓存 - "cacheDirectory":true
- 问题:当文件名没有发生变化的时候,同名文件都是走缓存。会导致修改内容与实际展示内容不一致。
- 解决:使用
hash命名,通过更换文件名来判断哪些文件需要更新
- 文件缓存
- hash :每次
webpack构建时会生成一个唯一的hash值 - 问题:因为
js和css同时使用一个hash值。如果重新打包,会导致所有的缓存失效。但是我可能是只改动了一个文件 - chunkhash:根据
chunk生成的hash值,如果打包来源于同一个chunk,那么hash值就一样 - 问题:
js和css的hash值还是一样的,因为css是在js中被引入的,所以同属于一个chunk - contenthash:根据文件的内容生成
hash值,不同文件的hash值一定不一样
代码实现
//绝对路径解析方法
const { resolve } = require("path");
//提取css成单独文件插件
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
//最新压缩css插件
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
//手动添加webpack内部js压缩器
const TerserWebpackPlugin = require('terser-webpack-plugin');
//对html文件处理
const HtmlWebpackPlugin = require('html-webpack-plugin');
//默认使用生产环境,定义nodejs环境变量:决定使用browserslist的哪个环境
process.env.NODE_ENV = 'production'
//复用loader
const commonCssloader = [
MiniCssExtractPlugin.loader,//提取css为单独文件
'css-loader',
{//兼容性处理 还需要在package.json中定义browserslist
loader: 'postcss-loader',
options: {
postcssOptions: {
ident: 'postcss',
plugins: [
'postcss-preset-env'
]
}
}
}
]
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/bundle.[contenthash:10].js',
path:resolve(__dirname,'dist'),
clean: true,
publicPath:'./'
},
//压缩css
optimization: {
minimize: true,
minimizer: [
new TerserWebpackPlugin(),
new CssMinimizerPlugin()
]
},
module: {
rules: [
/**
* 正常来讲,一个文件只能被一个loader处理
* 当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序
* 在loader中设置enforce:'pre',最先执行
*/
{
//优化生产环境打包构建速度
//以下loader只会匹配一个
//注意:这里不能有两个配置处理同一种类型文件
oneOf: [
{
//css兼容性处理
test: /\.css$/,
use: [...commonCssloader],
},
{
//less兼容性处理
test: /\.less$/,
use: [...commonCssloader,
//use中loader执行从下往上,必须把这个处理放在css-loader与less-loader之间
'less-loader'//将less转成css文件
],
},
{
//js兼容性处理
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader', //配置在.babelrc文件中
options: {
//开启babel缓存,第二次构建时会读取之前的缓存
//问题:当文件名没有发生变化的时候,同名文件都是走缓存。会导致修改内容与实际展示内容不一致。
cacheDirectory:true
}
},
{
//图片处理
test: /\.(jpg|png|gif)$/,
loader: 'url-loader',
options: {
limit: 8 * 1024,//对8kb以下的图片进行base64处理
name: '[name].[hash:8].[ext]',//对处理后的图片重命名
outputPath: 'img', //设置处理后的图片路径
publicPath: './', //指定打包后的css图片的基础路径,
esModule:false //关闭es6模块化
}
},
{
//处理html中的图片问题
test: /\.html$/,
loader: 'html-loader',
options: {
esModule:false
}
},
// 打包iconfont字体图标,和打包图片类似
{
test:/\.ttf$/,
use: {
loader: 'file-loader',
options: {
esModule: false, // 新版本中esModule默认为true,会导致文件的地址变为[object Module],因此这里设置为false
name: '[name]_[hash:6].[ext]', // 输出的文件名为[原名称]_[哈希值].[原后缀]
outputPath: 'fonts/', // 文件存储路径(output.path + 值)(物理路径, 存储路径)
publicPath:'../fonts' , // 负责输出目录, 即打包后的写在磁盘的位置
// 输出解析文件的目录,url 相对于 HTML 页面(index.html所在文件夹的绝对路径 + 值)(文件引用路径就是看这个)
// 如果output设置了publicPath, options也设置了publicPath,优先以options的publicPath为主
// 是对页面引入资源的补充,比如img标签引入或者css引入等.
// 千万不能设错,应该观察文件和HTML页面的存储地址位置,进行设置,否则引用时地址会错误,找不到文件
// 一般只设置output的publicPath,方便统一管理
limit: 1024 // 限制当文件小于1KB的时候,就将文件转为base64存储于js中,以减少http请求次数,当文件大于1KB,则打包文件到指定目录,避免js过大
}
}
},
{
//处理其他文件
exclude: /\.(js|css|less|html|jpg|png|gif|ttf)$/,
loader: 'file-loader',
options: {
name: '[name].[hash:8].[ext]',
outputPath:'media'
},
// type: 'asset/resource',
// generator: {
// filename: 'media/[name].[hash:6].[ext]'
// }
},
]
}
]
},
devtool: 'source-map',//错误追踪
plugins: [
//提取css为单独文件
new MiniCssExtractPlugin({
filename:'css/built.[contenthash:10].css'
}),
//对html文件处理
new HtmlWebpackPlugin({
template: './src/index.html',
scriptLoading: 'blocking',//去除script defer模式
//html压缩
mimify: {
//移除空格
collaspeWhitespace: true,
//移除注释
removeComments:true
}
})
],
//mode为production,js就自动压缩了
mode: 'production'
}
tree shaking
定义
- 将应用程序想象成一棵树。实际使用的源代码和库表示树的绿色活叶。死代码表示秋天被树木消耗的褐色枯叶,为了摆脱枯叶,必须摇晃树,使它们掉落
- 官方文档
作用
tree shaking:去除无用代码,
使用方法
- 使用
ES2015模块语法(即import和export) - 开启
production环境 (webpack4还必须使用es6模块化) - 在
package.json中配置无副作用的文件{ "name":"your-project", "sideEffects": ["*.css","*.less","./src/xxx"], }
要点记录
- “副作用”:在导入时执行特殊行为的代码,而不公开一个或多个导出。
"sideEffects":false
- 所有代码都没有副作用(都可以进行tree shaking)问题:可能会把css文件删掉
"sideEffects": true所有文件都有副作用,
- 全都不可 tree-shaking
"sideEffects": ["*.css","*.less"]
- 除了css,less文件有副作用,所有其他文件都可以 tree-shaking,但会保留这些文件
"sideEffects"类似于,/*#__PURE__*/但在模块级别而不是语句级别。- 通过使用
/*#__PURE__*/注释,可以告诉webpack函数调用是无副作用的(纯净的)。可以将其放在函数调用之前,以将其标记为无副作用。传递给函数的参数不会被注释标记,可能需要单独标记。当未使用变量的变量声明中的初始值被视为无副作用(纯净)时,它将被标记为无效代码,不被minimizer执行并丢弃,optimization.innerGraph设置为true的时候启用。/*#__PURE__*/ double(55);
代码实现
//绝对路径解析方法
const { resolve } = require("path");
//提取css成单独文件插件
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
//最新压缩css插件
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
//手动添加webpack内部js压缩器
const TerserWebpackPlugin = require('terser-webpack-plugin');
//对html文件处理
const HtmlWebpackPlugin = require('html-webpack-plugin');
//默认使用生产环境,定义nodejs环境变量:决定使用browserslist的哪个环境
process.env.NODE_ENV = 'production'
//复用loader
const commonCssloader = [
MiniCssExtractPlugin.loader,//提取css为单独文件
'css-loader',
{//兼容性处理 还需要在package.json中定义browserslist
loader: 'postcss-loader',
options: {
postcssOptions: {
ident: 'postcss',
plugins: [
'postcss-preset-env'
]
}
}
}
]
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/bundle.[contenthash:10].js',
path:resolve(__dirname,'dist'),
clean: true,
publicPath:'./'
},
//压缩css
optimization: {
minimize: true,
minimizer: [
new TerserWebpackPlugin(),
new CssMinimizerPlugin()
]
},
module: {
rules: [
{
oneOf: [
{
//css兼容性处理
test: /\.css$/,
use: [...commonCssloader],
},
{
//less兼容性处理
test: /\.less$/,
use: [...commonCssloader,
//use中loader执行从下往上,必须把这个处理放在css-loader与less-loader之间
'less-loader'//将less转成css文件
],
},
{
//js兼容性处理
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader', //配置在.babelrc文件中
options: {
//开启babel缓存,第二次构建时会读取之前的缓存
//问题:当文件名没有发生变化的时候,同名文件都是走缓存。会导致修改内容与实际展示内容不一致。
cacheDirectory:true
}
},
{
//图片处理
test: /\.(jpg|png|gif)$/,
loader: 'url-loader',
options: {
limit: 8 * 1024,//对8kb以下的图片进行base64处理
name: '[name].[hash:8].[ext]',//对处理后的图片重命名
outputPath: 'img', //设置处理后的图片路径
publicPath: './', //指定打包后的css图片的基础路径,
esModule:false //关闭es6模块化
}
},
{
//处理html中的图片问题
test: /\.html$/,
loader: 'html-loader',
options: {
esModule:false
}
},
// 打包iconfont字体图标,和打包图片类似
{
test:/\.ttf$/,
use: {
loader: 'file-loader',
options: {
esModule: false, // 新版本中esModule默认为true,会导致文件的地址变为[object Module],因此这里设置为false
name: '[name]_[hash:6].[ext]', // 输出的文件名为[原名称]_[哈希值].[原后缀]
outputPath: 'fonts/', // 文件存储路径(output.path + 值)(物理路径, 存储路径)
publicPath:'../fonts' , // 负责输出目录, 即打包后的写在磁盘的位置
// 输出解析文件的目录,url 相对于 HTML 页面(index.html所在文件夹的绝对路径 + 值)(文件引用路径就是看这个)
// 如果output设置了publicPath, options也设置了publicPath,优先以options的publicPath为主
// 是对页面引入资源的补充,比如img标签引入或者css引入等.
// 千万不能设错,应该观察文件和HTML页面的存储地址位置,进行设置,否则引用时地址会错误,找不到文件
// 一般只设置output的publicPath,方便统一管理
limit: 1024 // 限制当文件小于1KB的时候,就将文件转为base64存储于js中,以减少http请求次数,当文件大于1KB,则打包文件到指定目录,避免js过大
}
}
},
{
//处理其他文件
exclude: /\.(js|css|less|html|jpg|png|gif|ttf)$/,
loader: 'file-loader',
options: {
name: '[name].[hash:8].[ext]',
outputPath:'media'
},
// type: 'asset/resource',
// generator: {
// filename: 'media/[name].[hash:6].[ext]'
// }
},
]
}
]
},
devtool: 'source-map',//错误追踪
plugins: [
//提取css为单独文件
new MiniCssExtractPlugin({
filename:'css/built.[contenthash:10].css'
}),
//对html文件处理
new HtmlWebpackPlugin({
template: './src/index.html',
scriptLoading: 'blocking',//去除script defer模式
//html压缩
mimify: {
//移除空格
collaspeWhitespace: true,
//移除注释
removeComments:true
}
})
],
//mode为production,js就自动压缩了
mode: 'production'
}
code split
- 可以将代码分成多个
bundles,然后可以按需或并行加载,可以用于实现更小的bundle和控制资源加载优先级 - 官方文档
entry points(入口起点)
定义
- 使用
entry配置手动地分离代码 - 官方文档
使用方法
- 将入口设置为一个对象
- 更改输出文件名
//单入口 //entry: './src/js/index.js', entry: { index: './src/index.js', another: './src/another-module.js', }, output: { //filename: 'main.js', filename: '[name].bundle.js' },
要点记录
- 多入口:一个入口 ,输出就有一个
bundle - 问题:如果入口
chunk之间包含一些重复的模块,那些重复模块都会被引入到各个bundle中。 - 这种方法不够灵活,并且不能动态地将核心应用程序逻辑中的代码拆分出来。
代码实现
/**
* code split entry point
* 入口为一个对象
* entry:{}
*/
//绝对路径解析方法
const { resolve } = require("path");
//对html文件处理
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
//单入口
//entry: './src/js/index.js',
entry: {
//多入口:一个入口 --> 输出就有一个bundle
index: './src/js/index.js',
add: './src/js/add.js'
},
output: {
//[name]:取文件名
filename: 'js/[name].[contenthash:10].js',
path:resolve(__dirname,'dist'),
clean: true,
publicPath:'./'
},
plugins: [
//对html文件处理
new HtmlWebpackPlugin({
template: './src/index.html',
scriptLoading: 'blocking',//去除script defer模式
})
],
mode: 'development',
}
prevent duplication(防止重复)
定义
- 使用
Entry dependencies或者SplitChunksPlugin去重和分离chunk - 官方文档
使用方法
Entry dependencies
- 配置
dependOn option选项然后再加上optimization.runtimeChunk: 'single',否则会出问题,这样可以在多个chunk之间共享模块
除了生成module.exports = { mode: 'development', entry: { index: { import: './src/index.js', dependOn: 'shared', }, another: { import: './src/another-module.js', dependOn: 'shared', }, shared: 'lodash', }, output: { filename: '[name].bundle.js' }, optimization: { runtimeChunk: 'single', }, };shared.bundle.js,index.bundle.js和another.bundle.js之外,还生成了一个runtime.bundle.js文件
SplitChunksPlugin
SplitChunksPlugin插件可以将公共的依赖模块提取到现有的入口chunk中,或者提取到一个新生成的chunk。让我们使用这个插件,将在此之前的示例中重复的lodash模块移除
有了module.exports = { mode: 'development', entry: { index: './src/index.js', another: './src/another-module.js', }, output: { filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist'), }, optimization: { splitChunks: { chunks: 'all', }, }, };optimization.splitChunks配置选项后,我们现在应该看到从index.bundle.js中删除了重复的依赖项another.bundle.js。插件将lodash分离到单独的块
要点记录
- 尽管可以在
webpack中允许每个页面使用多入口,应确保避免使用多入口的入口:entry: { page: ['./analytics', './app'] }。如此,在使用async脚本标签时,会有更好的优化以及一致的执行顺序 index.js文件这样引入jquery
直接打包会将import $ from 'jquery'; console.log($);jquery和index.js打包成一个文件,加上splitChunks.chunks为all,就能将引入的第三方库,单独打包出来,还能分析多入口chunk中有没有公共文件(>30kb),有就单独打包
代码实现
/**
* prevent duplication(防止重复)
*/
//绝对路径解析方法
const { resolve } = require("path");
//对html文件处理
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
//单入口
entry: './src/js/index.js',
// entry: {
// index: './src/js/index.js',
// add: './src/js/add.js'
// },
output: {
//[name]:取文件名
filename: 'js/[name].[contenthash:10].js',
path:resolve(__dirname,'dist'),
clean: true,
publicPath:'./'
},
optimization: {
/**
* 单入口只有1,多入口1、2都有
* 1. 可以将node_modules中代码单独打包成一个chunk最终输出
* 2. 自动分析多入口chunk中有没有公共的文件(>30kb),如果有会单独打包成一个chunk
*/
splitChunks: {
chunks:'all'
}
},
plugins: [
//对html文件处理
new HtmlWebpackPlugin({
template: './src/index.html',
scriptLoading: 'blocking',//去除script defer模式
})
],
mode: 'development',
}
import(动态引入)
定义
使用方法
- 通过
js代码,让某个文件被单独打包成一个chunk
import动态导入语法:能将某个文件单独打包/** * 通过js代码,让某个文件被单独打包成一个chunk * import动态导入语法:能将某个文件单独打包 */ import(/* webpackChunkName:'add' */'./add') .then(({ mul, count }) => { console.log('add文件加载成功'); console.log("3+9=" + mul(3, 9)); console.log("3-9="+count(3,9)); }) .catch(() => { console.log('文件加载失败'); })
要点记录
- 语法
import(/* webpackChunkName:'test' */'./test')
代码实现
/**
* code split
* 单入口,import()动态引入
*/
//绝对路径解析方法
const { resolve } = require("path");
//对html文件处理
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
//单入口
entry: './src/js/index.js',
output: {
//[name]:取文件名
filename: 'js/[name].[contenthash:5].js',
path:resolve(__dirname,'dist'),
clean: true,
publicPath:'./'
},
plugins: [
//对html文件处理
new HtmlWebpackPlugin({
template: './src/index.html',
scriptLoading: 'blocking',//去除script defer模式
})
],
mode: 'development',
}
lary loading
定义
- 懒加载 前提:进行代码分割,当文件需要使用时才加载
- 官方文档
- 预加载
prefetch:会在使用之前,提前加载js文件 使用慎之又慎。详细- 正常加载可以认为是并行加载(同一时间加载多个文件)
- 预加载 :等其他资源加载完毕,浏览器空闲了,再偷偷加载资源
使用方法
- 通过
js代码,让某个文件被单独打包成一个chunk
import动态导入语法:能将某个文件单独打包document.getElementById('btn').onclick = function () { import(/* webpackChunkName:'add',webpackPrefetch:true */'./add') .then(({ mul }) => { console.log(mul(4, 5)); }) }
要点记录
- 增加一个交互,当用户单击按钮的时候用
console打印一些文字,但是会等到第一次交互的时候再加载那个代码块(print.js)
代码实现
/**
* lary 懒加载
*/
//绝对路径解析方法
const { resolve } = require("path");
//对html文件处理
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
//单入口
entry: './src/js/index.js',
output: {
//[name]:取文件名
filename: 'js/[name].[contenthash:10].js',
path:resolve(__dirname,'dist'),
clean: true,
publicPath:'./'
},
optimization: {
splitChunks: {
chunks:'all'
}
},
plugins: [
//对html文件处理
new HtmlWebpackPlugin({
template: './src/index.html',
scriptLoading: 'blocking',//去除script defer模式
//html压缩
mimify: {
//移除空格
collaspeWhitespace: true,
//移除注释
removeComments:true
}
})
],
//mode为production,js就自动压缩了
mode: 'production',
}
PWA
定义
- 渐进式Web应用程序(或
PWA)是可提供与本机应用程序相似的体验的Web应用程序。有很多事情可以促成这一点。在这些功能中,最重要的是使应用程序能够在离线状态下运行的能力,这是通过使用称为Service Workers的网络技术来实现的 - 官方文档
使用方法
- 安装插件
npm i workbox-webpack-plugin -D - webpack.config.js中添加
new WorkboxWebpackPlugin.GenerateSW({ /** * 1. 帮助serviceworker快速启动 * 2. 删除旧的 serviceworker * * 生成一个 serviceworker 配置文件 index.js中注册 */ clientsClaim: true, skipWaiting:true }) - index.js中注册serviceWorker 处理兼容性问题
if ('serviceWorker' in navigator) { window.addEventListener('load', () => { navigator.serviceWorker .register('/service-worker.js') .then(() => { console.log('sw注册成功了'); }) .catch(() => { console.log('sw注册失败了'); }) }) } - 安装
npm i serve -g - 启动服务器,将
dist目录下所有资源作为静态资源暴露出去 把网络设置为离线之后就可以在application中查看serviceworker中的文件了,使得网站可以离线访问serve -s dist
要点记录
serviceworker代码必须运行在服务器上
代码实现
/**
* PWA:渐进式网络开发应用程序(离线可访问)
* workbox --> workbox-webpack-plugin
*/
// workbox 下载插件
const WorkboxWebpackPlugin =require('workbox-webpack-plugin')
//绝对路径解析方法
const { resolve } = require("path");
//提取css成单独文件插件
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
//最新压缩css插件
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
//手动添加webpack内部js压缩器
const TerserWebpackPlugin = require('terser-webpack-plugin');
//对html文件处理
const HtmlWebpackPlugin = require('html-webpack-plugin');
//默认使用生产环境,定义nodejs环境变量:决定使用browserslist的哪个环境
process.env.NODE_ENV = 'production'
//复用loader
const commonCssloader = [
MiniCssExtractPlugin.loader,//提取css为单独文件
'css-loader',
{//兼容性处理 还需要在package.json中定义browserslist
loader: 'postcss-loader',
options: {
postcssOptions: {
ident: 'postcss',
plugins: [
'postcss-preset-env'
]
}
}
}
]
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/bundle.[contenthash:10].js',
path:resolve(__dirname,'dist'),
clean: true,
publicPath:'./'
},
//压缩css
optimization: {
minimize: true,
minimizer: [
new TerserWebpackPlugin(),
new CssMinimizerPlugin()
]
},
module: {
rules: [
/**
* 正常来讲,一个文件只能被一个loader处理
* 当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序
* 在loader中设置enforce:'pre',最先执行
*/
{
//优化生产环境打包构建速度
//以下loader只会匹配一个
//注意:这里不能有两个配置处理同一种类型文件
oneOf: [
{
//css兼容性处理
test: /\.css$/,
use: [...commonCssloader],
},
{
//less兼容性处理
test: /\.less$/,
use: [...commonCssloader,
//use中loader执行从下往上,必须把这个处理放在css-loader与less-loader之间
'less-loader'//将less转成css文件
],
},
{
//js兼容性处理
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader', //配置在.babelrc文件中
options: {
//开启babel缓存,第二次构建时会读取之前的缓存
//问题:当文件名没有发生变化的时候,同名文件都是走缓存。会导致修改内容与实际展示内容不一致。
cacheDirectory:true
}
},
{
//图片处理
test: /\.(jpg|png|gif)$/,
loader: 'url-loader',
options: {
limit: 8 * 1024,//对8kb以下的图片进行base64处理
name: '[name].[hash:8].[ext]',//对处理后的图片重命名
outputPath: 'img/', //设置处理后的图片路径
publicPath: './img', //指定打包后的css图片的基础路径,
esModule:false //关闭es6模块化
}
},
{
//处理html中的图片问题
test: /\.html$/,
loader: 'html-loader',
options: {
esModule: false
}
},
// 打包iconfont字体图标,和打包图片类似
{
test:/\.ttf$/,
loader: 'file-loader',
options: {
esModule: false, // 新版本中esModule默认为true,会导致文件的地址变为[object Module],因此这里设置为false
name: '[name]_[hash:6].[ext]', // 输出的文件名为[原名称]_[哈希值].[原后缀]
outputPath: 'fonts/', // 文件存储路径(output.path + 值)(物理路径, 存储路径)
publicPath:'../fonts' , // 负责输出目录, 即打包后的写在磁盘的位置
// 输出解析文件的目录,url 相对于 HTML 页面(index.html所在文件夹的绝对路径 + 值)(文件引用路径就是看这个)
// 如果output设置了publicPath, options也设置了publicPath,优先以options的publicPath为主
// 是对页面引入资源的补充,比如img标签引入或者css引入等.
// 千万不能设错,应该观察文件和HTML页面的存储地址位置,进行设置,否则引用时地址会错误,找不到文件
// 一般只设置output的publicPath,方便统一管理
limit: 1024 // 限制当文件小于1KB的时候,就将文件转为base64存储于js中,以减少http请求次数,当文件大于1KB,则打包文件到指定目录,避免js过大
}
},
{
//处理其他文件
exclude: /\.(js|css|less|html|jpg|png|gif)$/,
loader: 'file-loader',
options: {
// esModule: false,
name: '[name].[hash:8].[ext]',
outputPath: 'media/',
publicPath:'../media'
},
// type: 'asset/resource',
// generator: {
// filename: 'media/[name].[hash:6].[ext]'
// }
},
]
}
]
},
plugins: [
//提取css为单独文件
new MiniCssExtractPlugin({
filename:'css/built.[contenthash:10].css'
}),
//对html文件处理
new HtmlWebpackPlugin({
template: './src/index.html',
scriptLoading: 'blocking',//去除script defer模式
//html压缩
mimify: {
//移除空格
collaspeWhitespace: true,
//移除注释
removeComments:true
}
}),
new WorkboxWebpackPlugin.GenerateSW({
/**
* 1. 帮助serviceworker快速启动
* 2. 删除旧的 serviceworker
*
* 生成一个 serviceworker 配置文件 index.js中注册
*/
clientsClaim: true,
skipWaiting:true
})
],
//mode为production,js就自动压缩了
mode: 'production',
devtool:'source-map'
}
多进程打包
定义
- 在大项目中使用
thread-loader,提升构建速度 - 官方文档
使用方法
- 安装插件
npm i thread-loader -D - 开启多进程打包,在
babel-loader前面添加,可以在options中进行配置{ loader: 'thread-loader', // options: { // workers:2 // 产生的工作线程的数量,默认为(cpu数量- 1)或当require('os').cpus()未定义时,返回1 // } },
要点记录
js一般比较多,一般给babel-loader用,工作时间最长的loader,所以用thread-loader优化- 使用了这个
loader之后:loaders不能发出文件loaders不能使用自定义loaderAPI(即通过插件)loaders无法访问webpack options- 项目较大,打包较慢,开启多进程能提高速度
- 项目较少,打包很快,开启多进程会降低速度,进程启动大概为600ms,进程通信也有开销
代码实现
/**
* 多进程打包
*/
// workbox 下载插件
const WorkboxWebpackPlugin =require('workbox-webpack-plugin')
//绝对路径解析方法
const { resolve } = require('path');
//提取css成单独文件插件
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
//最新压缩css插件
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
//手动添加webpack内部js压缩器
const TerserWebpackPlugin = require('terser-webpack-plugin');
//对html文件处理
const HtmlWebpackPlugin = require('html-webpack-plugin');
//默认使用生产环境,定义nodejs环境变量:决定使用browserslist的哪个环境
process.env.NODE_ENV = 'production'
//复用loader
const commonCssloader = [
MiniCssExtractPlugin.loader,//提取css为单独文件
'css-loader',
{//兼容性处理 还需要在package.json中定义browserslist
loader: 'postcss-loader',
options: {
postcssOptions: {
ident: 'postcss',
plugins: [
'postcss-preset-env'
]
}
}
}
]
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/bundle.[contenthash:10].js',
path:resolve(__dirname,'dist'),
clean: true,
publicPath:'./'
},
//压缩css
optimization: {
minimize: true,
minimizer: [
new TerserWebpackPlugin(),
new CssMinimizerPlugin()
]
},
module: {
rules: [
/**
* 正常来讲,一个文件只能被一个loader处理
* 当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序
* 在loader中设置enforce:'pre',最先执行
*/
{
//优化生产环境打包构建速度
//以下loader只会匹配一个
//注意:这里不能有两个配置处理同一种类型文件
oneOf: [
{
//css兼容性处理
test: /\.css$/,
use: [...commonCssloader],
},
{
//less兼容性处理
test: /\.less$/,
use: [...commonCssloader,
//use中loader执行从下往上,必须把这个处理放在css-loader与less-loader之间
'less-loader'//将less转成css文件
],
},
{
//js兼容性处理
test: /\.js$/,
exclude: /node_modules/,
use: [
/**
* 开启多进程打包
* 进程启动大概为600ms,进程通信也有开销
* 只有工作消耗时间比较长,才需要多进程打包
* js一般比较多,工作时间最长的loader,所以用thread-loader优化
*/
{
loader: 'thread-loader',
options: {
workers:2 //进程2个 ,产生的工作线程的数量,默认为(cpu数量- 1)CPU核数减一
}
},
{
loader: 'babel-loader', //配置在.babelrc文件中
options: {
//开启babel缓存
cacheDirectory:true
}
}
]
},
{
//图片处理
test: /\.(jpg|png|gif)$/,
loader: 'url-loader',
options: {
limit: 8 * 1024,//对8kb以下的图片进行base64处理
name: '[name].[hash:8].[ext]',//对处理后的图片重命名
outputPath: 'img/', //设置处理后的图片路径
publicPath: './img', //指定打包后的css图片的基础路径,
esModule:false //关闭es6模块化
}
},
{
//处理html中的图片问题
test: /\.html$/,
loader: 'html-loader',
options: {
esModule:false
}
},
{
//处理其他文件
exclude: /\.(js|css|less|html|jpg|png|gif)$/,
loader: 'file-loader',
options: {
name: '[name].[hash:8].[ext]',
outputPath: 'media/',
publicPath:'../media'
},
// type: 'asset/resource',
// generator: {
// filename: 'media/[name].[hash:6].[ext]'
// }
},
]
}
]
},
plugins: [
//提取css为单独文件
new MiniCssExtractPlugin({
filename:'css/built.[contenthash:10].css'
}),
//对html文件处理
new HtmlWebpackPlugin({
template: './src/index.html',
scriptLoading: 'blocking',//去除script defer模式
//html压缩
mimify: {
//移除空格
collaspeWhitespace: true,
//移除注释
removeComments:true
}
}),
new WorkboxWebpackPlugin.GenerateSW({
/**
* 1. 帮助serviceworker快速启动
* 2. 删除旧的 serviceworker
*
* 生成一个 serviceworker 配置文件 index.js中注册
*/
clientsClaim: true,
skipWaiting:true
})
],
//mode为production,js就自动压缩了
mode: 'production',
devtool:'source-map'
}
external
定义
Externals配置项用来告诉Webpack要构建的代码中使用了哪些不用被打包的模块,也就是说这些模版是外部环境提供的,Webpack在打包时可以忽略它们- 官方文档
使用方法
- 在module.exports中添加,要排除的包名
externals: { //拒绝jQuery被打包进来 在html界面通过script标签引入cdn链接 jquery:'jQuery' } - 在html界面通过script标签引入cdn链接
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
要点记录
- cdn
boostrapcdn开源网站,优化webpack打包 external可以防止框架的二次引用
代码实现
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/bundle.js',
path: resolve(__dirname, 'dist')
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
})
],
mode: 'production',
externals: {
//拒绝jQuery被打包进来 在html界面通过script标签引入cdn链接
jquery:'jQuery'
}
};
写在后面
优化配置,这里终于告一段落,学习的过程中有很多都已经变化了,所以附了很多官方文档的链接,还是要结合最新的文档配合学习。
未选择的路,心里永远会有一个设想,如果当初选择了另一条路会怎样,其实无论是什么样子,总不会是自己在这种情境下期待的样子,当你身处局中时,你的选择改变不了未来的,我们只能走一条路,在踏上的那一刻,就是一条全新的路了,未来因此刻的选择而变化,过去的结点没有办法回转。

本文详细介绍Webpack5的性能优化技巧,包括开发环境与生产环境的优化策略,如HMR、source-map、缓存机制、treeshaking、代码分割、多进程打包等,帮助开发者提升构建效率。
性能优化&spm=1001.2101.3001.5002&articleId=117079514&d=1&t=3&u=eaec46e2c14a41d7afc05c4be710775a)
2358

被折叠的 条评论
为什么被折叠?



