引言
webpack工程化的优化是十分经典且重要的问题,文章将从加快构建速度,减少打包体积两个角度介绍如何进行项目优化。
一:项目分析
进行webpack优化,首先要做的肯定是分析问题,而不是直接一顿321连招上去,如下是常用的项目分析插件。
1. 编译进度条
随着项目工程的复杂度提升,在编译构建项目时通常需要耗费几分钟,这个时候可以添加打包进度条为我们提供更好的视觉体验。
安装
npm i -D progress-bar-webpack-plugin
使用
webpack.common.js
const ProgressBarWebpackPlugin = require('progress-bar-webpack-plugin')
const config = {
plugins:[
new ProgressBarWebpackPlugin()
]
}
2. 编译速度分析
编译速度分析插件可以在打包构建时列出每个loader耗费的时间,开发者可以对耗时较长的loader进行针对性优化。
webpack.dev.js
安装
npm i -D speed-measure-webpack-plugin
使用
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
const smp = new SpeedMeasurePlugin();
const config = smp.wrap({
mode: 'development'
})
3. 打包体积分析
打包体积插件通过可视化的方式展示每个bundle具体的体积大小和所占的空间比例,开发者可对较大体积的bundle进行优化。
webpack.prod.js
安装
npm i -D webpack-bundle-analyzer
使用
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const config = {
mode: 'development',
plugins:[
new BundleAnalyzerPlugin()
]
}
二:提升构建速度
在开发阶段如果项目比较复杂,通常构建等待时间会比较长,这势必会影响开发效率,当然掌握了构建速度分析,你也可以悄悄的反向优化构建速度,提高摸鱼质量。
1. 升级webpack
webpack的每次升级都是对上一版本的优化,通常新版的构建效率都会被提高一个档次。
2. 添加cache缓存
添加cache缓存,在webpack第二次构建时可以大幅提高构建速度。
module.exports = {
cache: {
type: 'filesystem', // 使用文件缓存
},
}
3. 使用include资源匹配范围
module.exports = {
rules: [
{
test: /\.(js|ts|jsx|tsx)$/,
include: paths.appSrc,
use: [
{
loader: 'esbuild-loader',
options: {
loader: 'tsx',
target: 'es2015',
},
}
]
}
]
}
4.优化resolve
通过对resolve的alias,extensions,modules进行设置,加快文件匹配检索
module.exports = {
resolve: {
extensions: ['.tsx', '.ts', '.js'],
alias:{
'@':rootPath, // @ 代表 src 路径
},
modules: [ 'node_modules', paths.appSrc, ]
},
}
5.多进程打包
安装
npm install --save-dev thread-loader
使用
```
module.exports = {
module: {
rules: [
{
test: /.js$/,
include: path.resolve('src'),
use: [
"thread-loader",
// 耗时的 loader (例如 babel-loader)
],
},
],
},
};
```
6.代码映射
开发阶段通常使用eval-cheap-module-source-map
export.module = {
devtool: 'eval-cheap-module-source-map',
}
三:项目体积优化
1.js压缩
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
optimization: {
minimizer: [
new TerserPlugin({
parallel: 4,
terserOptions: {
parse: {
ecma: 8,
},
compress: {
ecma: 5,
warnings: false,
comparisons: false,
inline: 2,
},
mangle: {
safari10: true,
},
output: {
ecma: 5,
comments: false,
ascii_only: true,
},
},
}),
]
}
}
2.css压缩
安装
npm install -D css-minimizer-webpack-plugin
使用
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
module.exports = {
optimization: {
minimizer: [
new CssMinimizerPlugin()
]
}
}
3.js tree-shaking
webpack默认在production模式下开启了tree-shaking,但是通常我们需要自定义哪些文件不被tree-shaking掉。
例子1
//tool.js
export function add(a,b) {
return a+b;
}
export function del(a,b){
return a-b;
}
//index.js
import {add,del} from "./tool.js"
console.log(add(1,2));
上面的例子在打包index.js时del方法会被过滤掉。
例子2
import "./styles.css"
import "./tool.js"
上面两种导入方式在webpack打包时都会被过滤掉,但是通常在项目中经常使用这种导入css的方式,因此我们必须自定义配置哪些文件不会被tree-shaing掉。
设置方式
修改package.json
第一种方式表示所有的模块都放心的执行tree-shaking规则(导入没有使用就过滤),第二种方式表示当前数组匹配的文件不会被过滤掉。这也就解决了css直接导入被过滤的问题了。
"sideEffects": false
"sideEffects": ["*.css"]
4.css tree-shaking
安装
npm i purgecss-webpack-plugin -D
使用
const PurgeCSSPlugin = require('purgecss-webpack-plugin');
const config = {
plugins:[
new PurgeCSSPlugin({//css tree-shaking
paths: glob.sync(`${rootPath}/**/*`, { nodir: true }),
}),
]
}
5. css代码分离
在打包时通常css体积比较大,因此需要将项目中所有的css单独抽离打包
安装
npm install -D mini-css-extract-plugin
使用
webpack.common.js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
plugins:[
new MiniCssExtractPlugin({
filename: "[name].css",
chunkFilename: "[id].css",
})
]
module:{
rules:[
{
test: /\.css$/i,
use: [MiniCssExtractPlugin.loader, "css-loader"],
include: rootPath,
},
]
}
7. cdn加载
对于第三方模块,例如lodash,jquery我们可以通过cdn并行加载,大幅减少打包体积。通过cdn加载的模块不会打包到最终bundle中。
-
html引入cdn
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>webpack You hua</title>
<script src="https://code.jquery.com/jquery-3.1.0.js"></script>
</head>
<body>
</body>
</html>
-
使用externals
externals: {//将jQuery通过cdn引入,不会打包到bundle文件中 jquery: 'jQuery', }, -
使用
import $ from "jquery"
8. 删除第三方文件多余依赖
例如moment.js插件,其包体积包含需要无用的语言包,我们可以借助第三方插件删除无用依赖
const MomentLocalesPlugin = require('moment-locales-webpack-plugin');
plugins:[
new MomentLocalesPlugin({//momnet删除无用的语言包
localesToKeep: ['zh-cn'],
}),
]
9. 第三方工具的筛选
- 优先选择支持esm模块的工具,esm模块支持tree-shaking
- 优先选择体积小,且稳定的工具。例如尽量选择day.js而不是moment.js
10. splitChunks分包处理
module.exports = {
optimization:{
splitChunks: {
// include all types of chunks
chunks: 'all',
// 重复打包问题
cacheGroups:{
vendors:{ // node_modules里的代码
test: /[\\/]node_modules[\\/]/,
chunks: "all",
// name: 'vendors', 一定不要定义固定的name
priority: 10, // 优先级
enforce: true
}
}
},
}
}