webpack构建速度和体积优化策略
初级分析: 使用webpack内置的stats
- stats: 构建的统计信息
- pakeage.json 使用stats
方式一:
"scripts": {
"build:stats": "webpack --env production --json > stats.json"
},
方式二:
// node.js中使用
const prodConfig = require('../../lib/webpack.prod.js');
webpack(prodConfig, (err, stats) => {
if (err) {
console.error(err);
process.exit(2);
}
console.log(stats);
});
颗粒度太粗,看不出问题所在
速度分析: 使用 speed-measure-webpack-plugin
- 分析整个打包总耗时
- 每个插件和loader的耗时情况
const SpeedMeasureWebpackPlugin = require('speed-measure-webpack-plugin');
const smp = new SpeedMeasureWebpackPlugin();
module.exports = smp.wrap({
...
})
体积分析: 使用webpack-bundle-analyzer
- 可以分析哪些问题
- 依赖的第三方模块文件大小
- 业务里面的组件代码大小
const {BundleAnalyzerPlugin} = require('webpack-bundle-analyzer');
plugins:[
new BundleAnalyzerPlugin()
]
使用高版本的webpack和Node.js
多进程/多实例构建
- 使用thread-loader解析资源
{
test: /\.js$/,
use: [
{
loader: 'thread-loader',
options: {
workers: 3
}
},
'babel-loader'
]
}
原理: 每次webpack解析一个模块,thread-loader会将它及它的依赖分配给worker线程中
多进程并行压缩代码
使用 terser-webpack-plugin 开启parallel参数
optimization: {
minimizer: [
new TerserPlugin({
parallel: true
})
]
}
分包: 预编译资源模块
思路: 将基础包和业务组件包打成一个文件
方法: 使用DLLPlugin进行分包, DllReferencePlugin对manifest.json引用
// 新建webpack.dll.js
const path = require('path');
const webpack = require('webpack');
module.exports = {
entry: {
library: [
'react',
'react-dom'
]
},
output: {
filename: '[name]_[chunkhash].dll.js',
path: path.join(__dirname,'build/library'),
library: '[name]'
},
plugins: [
new webpack.DllPlugin({
name: '[name]_[hash]',
path: path.join(__dirname,'build/library/[name].json'),
})
]
}
// webpack.prod.js
plugins: [
new webpack.DllReferencePlugin({
manifest: require('./build/library/library.json')
})
]
充分利用缓存提升二次构建速度
- 缓存思路
- babel-loader开启缓存
{ test: /\.js$/, use: [ 'babel-loader?cacheDirectory=true' ] }
- terser-webpack-plugin开启缓存
optimization: { minimizer: [ new TerserPlugin({ parallel: true, cache: true }) ] }
- 使用cache-loader 或者 hard-source-webpack-plugin
plugins: [ new HardSourceWebpackPlugin() ]
- babel-loader开启缓存
缩小构建目标
- 目的: 尽可能的少构建模块。比如babel-loader不解析node_modules
{
test: /\.js$/,
include: path.resolve('src'),
exclude: 'node_modules',
use: [
'babel-loader'
]
},
- 减少文件搜索范围
- 优化 resolve.modules配置(减少模块搜索层级)
- 优化 resolve.mainFields配置
- 优化 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'), }, extensions: ['.js'], mainFiles: ['main'] }
Tree Shaking擦除无用的JavaScript和Css
-
- 擦除无用的JavaScript(见webpack全方位由浅到深讲解,做到简历上真正所谓的“熟悉”(系列一)
-
- 擦除无用的Css
- 使用 purgecss-webpack-plugin 和 mini-css-extract-plugin配合使用
const PurgecssWebpackPlugin = require('purgecss-webpack-plugin') const PATHS = { src: path.join(__dirname, 'src') } plugins: [ new MiniCssExtractPlugin({ filename: '[name][contenthash:8].css' }), new PurgecssWebpackPlugin({ paths: glob.sync(`${PATHS.src}/**/*`, {nodir: true}) }) ]
图片压缩
- 要求:基于 Node 库的imagemin或者tinypng API
- 使用:配置image-webpack-loader
rules: [{
test: /\.(gif|png|jpe?g|svg)$/i,
use: [
'file-loader',
{
loader: 'image-webpack-loader',
options: {
mozjpeg: {
progressive: true,
},
// optipng.enabled: false will disable optipng
optipng: {
enabled: false,
},
pngquant: {
quality: [0.65, 0.90],
speed: 4
},
gifsicle: {
interlaced: false,
},
// the webp option will enable WEBP
webp: {
quality: 75
}
}
},
],
}]
动态使用Polyfill
-
原理: 识别 User Agent, 下发不同的 Polyfill
-
如何使用动态 Polyfill service
- polyfill.io 官方提供的服务
<script src="https://cdn.polyfill.io/v2/polyfill.min.js"></script>
- 基于官方自建 polyfill服务