使用内置的stats
stats是构建的统计信息
// 添加命令
"build:stats": "webpack --config webpack.prod.js --json > stats.json"
生成stats文件,颗粒度比较粗。
打包速度分析
可以使用speed-measure-webpack-plugin插件
// webpack.prod.js中
// 1.定义插件对象
const smp = new SpeedMeasureWebpackPlugin();
// 2.包裹要分析的内容
module.exports = smp.wrap({
...
});
执行命令后,结果如下:
可以分析打包总耗时,分析每个插件和loader耗时的情况,红色需要优化。
打包体积分析
可以使用webpack-bundle-analyzer
const {BundleAnalyzerPlugin} = require('webpack-bundle-analyzer');
plugins: [
new BundleAnalyzerPlugin()
]
启动项目后会自动打开浏览器,根据提示进行体积分析
多进程/多实例构建
使用thread-loader,将打包任务划分为多个node进程,将模块分给多个进程
rules: [
{
test: /.js$/,
use: [
{
loader: 'thread-loader',
options: {
workers: 3
}
},
'babel-loader'
]
}
]
效果有待确认,目前项目小,还看不出来,反而反向优化了。(进程的开销也是需要时间的。在项目较小时,反而是负优化)
并行压缩
使用terser-webpack-plugin插件,开启parallel参数
optimization: {
minimizer: [
new TerserPlugin({
parallel: true
})
]
}
测试效果依旧不明显
预编译资源模块-dll
- 思路:将react、react-dom、redux、react-redux基础包和业务基础包打包成一个文件
- 方法:使用 DllPlugin 进行分包,DllReferencePlugin 对 manifest.json 引用
实现步骤:
1.新建webpack.dll.js
const path = require('path');
const webpack = require('webpack');
module.exports = {
entry: {
// 多个包可以在entry中新增一个key
bundleLibrary: [
'react',
'react-dom'
]
},
output: {
filename: '[name].dll.js',
// 输出的位置
path: path.join(__dirname, 'build/library'),
library: '[name]'
},
// 生成manifest.json
plugins: [
new webpack.DllPlugin({
name: '[name]_[hash]',
path: path.join(__dirname, 'build/library/[name].json')
})
]
}
2.新增命令行
"dll": "webpack --config webpack.dll.js"
执行命令后,文件新增
3.webpack.prod.js中引入
plugins: [
new webpack.DllReferencePlugin({
manifest: require('./build/library/bundleLibrary')
})
]
执行构建命令后,文件打包体积变小
问答:dll的方式好像在webpack4里面应用的不是很多了,webpack4已经做了优化,我查看了下vue-cli以及create-react-app都抛弃了这个配置,具体原因地址:github.com/vuejs/vue-c…
解答:是的,如果项目使用了 Webpack4,确实对 dll 的依赖没那么大,使用 dll 相对来说提升也不是特别明显。而且有 hard-source-webpack-plugin 可以极大提升二次构建速度。
不过从实际前端工程中来说, dll 还是很有必要掌握的。对于一个团队而言,基本是采用相同的技术栈,要么 React、要么Vue 等等。这个时候,通常的做法都是把公共框架打成一个 common bundle 文件供所有项目使用。比如我们团队会将 react、react-dom、redux、react-redux 等等打包成一个公共库。dll 可以很好的满足这种场景:将多个npm包打成一个公共包。因此团队里面的分包方案使用 dll 还是很有价值,常见的会从整个工程的角度分为基础包(react、redux等)、业务公共包(所有业务都要用到的监控上报脚本、页面初始化脚本)、某个业务的js。
问答:dllplugin和splitChunks可以一起用吗?有没有什么区别和联系?
解答:可以一起使用。 DllPlugin 通常用于基础包(框架包、业务包)的分离。
SplitChunks 虽然也可以做 DllPlugin 的事情,但是更加推荐使用 SplitChunks 去提取页面间的公共 js 文件。因为使用 SplitChunks 每次去提取基础包还是需要耗费构建时间的,如果是 DllPlugin 只需要预编译一次,后面的基础包时间都可以省略掉。
webpack5的新方式?
开启缓存
babel-loader 开启缓存
terser-webpack-plugin 开启缓存
使用 cache-loader 或者 hard-source-webpack-plugin
optimization: {
minimizer: [
new TerserPlugin({
parallel: true,
cache: true
})
]
}
运行后,在node_modules目录中会出现一个cache文件夹,再次执行就会缓存。
使用前:
使用后:
项目大会提升会更明显
缩小构建目标
- 尽可能少构建模块,比如 bebel-loader 不解析 node_modules
- 优化resolve.modules配置(减少模块搜索层级,就在配置的目录中找),从当前项目里面找,没找到会去node_modules中找,依次往父目录中找这些模块是否存在。比如第三方包我们放到node_modules中,那就没有必要从当前项目里面一层一层找。
- 优化resolve.mainFields配置,即优化入口文件,只查找package.json中的main字段
- 优化resolve.extensions配置(配置什么就只查找带该后缀的文件),不写后缀时默认匹配的后缀,减少各种试验的次数以增加速度
- 合理使用alias,在指定路径查找
resolve: {
alias: {
'react': path.resolve(__dirname, './node_modules/react/umd/react.production.min.js'),
'react-dom': path.resolve(__dirname, './node_modules/react-dom/umd/react-dom.production.min.js')
},
modules: [path.resolve(__dirname, 'node_modules')],
extensions: ['.js'],
mainFields: ['main']
}
rules: [
{
test: /.js$/,
// 只解析src,或者exclude: 'node_modules'
include: path.resolve('src'),
use: [
{
loader: 'thread-loader',
options: {
workers: 3
}
},
'babel-loader',
// 'eslint-loader'
]
}
]
擦除无用的css和图片压缩
擦除无用的css使用purgecss-webpack-plugin插件
图片压缩使用image-webpack-loader
使用动态polyfill服务优化构建体积
polyfill-service原理:根据浏览器的ua下发不同的polyfill。对于已经支持es6语法的浏览器版本,就不会反悔对应的polyfill