伴随着项目越来越大,相应的webpack的构建时间越来越久,使得我们不得不考虑性能优化的问题。如何进行分析呢?
这里列出笔者常用的工具,仅作参考
- 时间分析:speed-measure-webpack-plugin
- 体积分析:webpack-bundle-analyzer
以下将详细介绍优化的策略
打包时间
搜索方面
- 合理使用resolve
module.exports = {
// ...
resolve: {
extensions: ['.js', '.jsx'], // 自动带上后缀查找文件
mainFiles: ['index', 'list'],
alias: { // 别名,路径的映射,减少查找的范围
alias: path.resolve(__dirname, '../src/common'),
},
modules: [
path.resolve(__dirname, 'node_modules'), // 指定当前目录下的 node_modules 优先查找
'node_modules', // 将默认写法放在后面
]
},
}
- include/exclude
const path = require('path');
module.exports = {
//...
module: {
rules: [
{
test: /\.js[x]?$/,
use: ['babel-loader'],
include: [path.resolve(__dirname, 'src')],// 指定要包含的文件
exclude: /node_modules/, // 不需要包含node_modulues 模块
}
]
},
}
使用缓存提升二次打包速度
将结果缓存到磁盘中,再次构建时,会进行对比,如果文件较之前的没有发送改变,则直接读取缓存)
- 在性能消耗较大的loader之前添加==cache-loader==,将结果缓存到磁盘中。
module.exports = {
//...
module: {
//项目中,babel-loader耗时比较长,所以给它配置了`cache-loader`
rules: [
{
test: /\.jsx?$/,
use: ['cache-loader','babel-loader']
}
]
}
}
- loader开启cacheDirectory=true 缓存
module.exports = {
//...
new HappyPack({
loaders: ['babel-loader?cacheDirectory=true'],
})
}
- 静态资源开启缓存 ==(hash, chunkhash, contenthash)==
hash:所有的文件哈希值都相同;
chunkhash:根据不同的入口文件进行依赖文件解析、构建对应的 chunk,生成对应的哈希值;
contenthash: 计算与文件内容本身相关,主要用在css抽取css文件时;
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
mode: "production",
entry: {
index: "./src/index.js",
chunk1: "./src/chunk1.js"
},
output: {
filename: "[name].[chunkhash].js" // 不同的入口文件,对应不同的chunk
},
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader"
]
}
]
},
plugins: [
// 提取css插件
new MiniCssExtractPlugin({
filename: "[name].[contenthash].css" // 与文件内容本身相关联
})
]
};
开启多进程
HappyPack
thread-loader(webpack4及之后推荐使用)
module.exports = {
// ...
module: {
rules: [
{
test: /.js$/, //对所有js后缀的文件进行编译
use: [
// 'babel-loader'
'happypack/loader',
],
}
]
},
plugins: [
new HappyPack({
loaders: ['babel-loader'],
}),
]
}
module.exports = {
// ...
module: {
rules: [
{
test: /.js$/, //对所有js后缀的文件进行编译
include: path.resolve('src'), //表示在src目录下的.js文件都要进行一下使用的loader
use: [
'babel-loader',
{
loader: 'thread-loader',
options: {
workers: 3,
},
}
],
}
]
}
使用(动态链接库) DLLPlugin 和 DLLReferencePlugin 单独打包不经常更新的第三方库
打包体积
JS,CSS代码压缩
webpack-parallel-uglify-plugin:开始多进程压缩JS
mini-css-extract-plugin:抽离CSS,生成单独的文件
optimize-css-assets-webpack-plugin:压缩CSS文件
开启 tree-shaking(webpack4 默认开启)
静态资源压缩
对于文件使用file-loader
对于图片使用url-loader,limit属性可设置大小
module.exports = {
// ...
rules: [
// ...
{
test: /\.(png|svg|jpg|gif)$/,
use: [
// ...
{
loader: 'url-loader', //是指定使用的loader和loader的配置参数
options: {
limit:500, //是把小于500B的文件打成Base64的格式,写入JS
name: 'images/[name]_[hash:7].[ext]',
}
}
]
}
]
}
splitCHunksPlugin 代码分割,抽离公共业务代码
optimization: {
splitChunks: {
chunks: "async", // 必须三选一: "initial" | "all"(推荐) | "async" (默认就是async)
minSize: 30000, // 最小尺寸,30000
minChunks: 1, // 最小 chunk ,默认1
maxAsyncRequests: 5, // 最大异步请求数, 默认5
maxInitialRequests : 3, // 最大初始化请求书,默认3
automaticNameDelimiter: '~',// 打包分隔符
name: function(){}, // 打包后的名称,此选项可接收 function
cacheGroups:{ // 这里开始设置缓存的 chunks
priority: 0, // 缓存组优先级
vendor: { // key 为entry中定义的 入口名称
chunks: "initial", // 必须三选一: "initial" | "all" | "async"(默认就是async)
test: /react|lodash/, // 正则规则验证,如果符合就提取 chunk
name: "vendor", // 要缓存的 分隔出来的 chunk 名称
minSize: 30000,
minChunks: 1,
enforce: true,
maxAsyncRequests: 5, // 最大异步请求数, 默认1
maxInitialRequests : 3, // 最大初始化请求书,默认1
reuseExistingChunk: true // 可设置是否重用该chunk
}
}
}
},
网络请求方面
配置externals,静态文件使用CDN加速静态资源的加载
// 在 index.html 中手动引入 cdn 链接。
// webpack.js
module.exports = {
externals: {
'vue': 'Vue',
'element-ui': 'ELEMENT',
'at-ui': 'at'
}
}