本文适用于使用 webpack 或者基于 webpack 的脚手架(如:vue-cli)开发的项目。
特别说明:
本文仅简单罗列一些关于 webpack 的优化项,webpack 的发展速度非常快,当前的方案可能在未来已经无法使用,请知悉。文章编写时间:2021-11。本文所列举的示例均以webpack@4
为基础。
在 webpack 构建时,追加--profile --json > stats.json
,将得到stats.json
文件,然后使用官方的分析网站来分析该文件。我们将得到打包时间、模块数量以及每个模块的大小、错误和警告的列表及详情、chunk 数量等信息,然后用分析工具分析(分析工具充当的只是图形化、格式化展示的角色而已)出来的信息,大致的定位问题。
这是一个 webpack 的插件,将其配置到 webpack 的选项中即可。该工具能很直观的给出每一个打包出来的文件的大小以及各自的依赖和具体路径,能够更加方便的帮助我们对项目进行分析。请按需启用。
使用示例:
npm install --save-dev webpack-bundle-analyzer
// package.json
{
"scripts": {
"build-analyze": "cross-env NODE_ENV=production IS_ANALYZE=true npm run build"
}
}
const isAnalyze = !!process.env.IS_ANALYZE
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')
const plugins = [
// ...
]
if (isAnalyze) {
new BundleAnalyzerPlugin(),
}
const config = {
// ...
plugins,
// ...
}
这是一个 webpack 的插件,将其配置到 webpack 的选项中即可。它可以帮助我们分析整个打包的总耗时,以及每一个 loader 和每一个 plugins 构建所耗费的时间,从而帮助我们快速定位 webpack 配置中可以改进的问题。
使用示例:
npm install --save-dev speed-measure-webpack-plugin
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin')
const smp = new SpeedMeasurePlugin()
const webpackConfig = smp.wrap({
plugins: [
// ...
],
})
webpack 的每一个版本的更新,在其内部都做了大量的优化和重构。依惯例,升级版本一定能带来性能提升,而且提升效果很明显。比如:Tree Shaking(对 ES6 Modules 生效),在 webpack@4
的时候,就已经可以使用了。
缺点:
项目越大,需要转换代码越多,效率就越低。我们可以通过配置exlcude
的方式来减少待转换文件的数量。
使用示例:
const config = {
//...
module: {
rules: [
{
test: /\.m?js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
},
},
},
],
},
//...
}
HappyPack 通过并行转换文件使初始 webpack 构建更快。
npm install --save-dev happypack
const path = require('path')
const os = require('os')
const HappyPack = require('happypack')
const happyThreadPool = HappyPack.ThreadPool({
size: Math.floor(os.cpus().length * 0.8),
})
const config = {
// ...
plugins: [
// ...
new HappyPack({
id: 'js',
loaders: ['babel-loader'],
threadPool: happyThreadPool,
verbose: true,
}),
// ...
],
module: {
rules: [
// ...
{
test: /\.js[x]?$/,
include: [path.resolve('src')],
exclude: /node_modules/,
loader: 'happypack/loader?id=js',
},
// ...
],
},
// ...
}
这是 webpack 内置的优化方法。类似于 windows 的 dll 文件。
使用示例:
// webpack.dll.config.js
const webpack = require('webpack')
const os = require('os')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const path = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const TerserPlugin = require('terser-webpack-plugin')
const {
projectPath,
packageJSON,
dllConfig,
dllPath,
isProduction,
} = require('./consts')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
const stats = require('./webpack-config/stats.js')
const dllConfigPath = path.join(projectPath, 'dll.config.js')
const dllConfig = require(dllConfigPath)
const { dllArray } = dllConfig
module.exports = {
context: projectPath,
entry: {
vendor1: [
'vue',
'vue-router',
'vuex',
'vue-class-component',
'vuex-class',
'vue-property-decorator',
'axios',
'throttle-debounce',
],
vendor2: [...dllArray],
},
output: {
path: path.join(projectPath, dllPath),
filename: '[name].dll.js',
pathinfo: true,
library: '[name]_dll',
},
resolve: {
extensions: ['.ts', '.js', '.vue'],
mainFiles: ['index'],
modules: [path.resolve(projectPath, 'node_modules'), 'node_modules'],
},
stats,
plugins: [
new CleanWebpackPlugin({
verbose: true,
cleanStaleWebpackAssets: false,
cleanOnceBeforeBuildPatterns: ['**/*'],
}),
new VueLoaderPlugin(),
new webpack.DllPlugin({
context: projectPath,
path: path.resolve(projectPath, dllPath, 'manifest.json'),
name: '[name]_dll',
}),
],
optimization: {
minimizer: [
new TerserPlugin({
test: /\.js$/i,
parallel: os.cpus().length,
sourceMap: true,
extractComments: false,
terserOptions: {
ecma: 5,
warnings: false,
parse: {},
compress: {},
mangle: true,
keep_classnames: false,
keep_fnames: false,
output: {
beautify: false,
comments: false,
// 只用单引号
quote_style: '1',
},
},
}),
],
},
module: {
rules: [
{ test: /\.vue$/, use: ['vue-loader'] },
{
test: /\.s?css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'sass-loader',
'postcss-loader',
],
},
{
test: /\.ts$/,
loader: 'ts-loader',
options: { appendTsSuffixTo: [/\.vue$/] },
exclude: /node_modules/,
},
{
test: /\.js$/,
exclude: /node_modules/,
use: ['babel-loader'],
},
],
},
}
// dll.config.js
module.exports = {
dllArray: [
'muse-ui',
'muse-ui-message',
'muse-ui-toast',
'numbro',
'vue-awesome-swiper',
'vue-dplayer',
'vue-json-viewer',
'vue-star-rating',
'vuedraggable',
'weixin-js-sdk',
],
}
// package.json
{
"scripts": {
"build-dll": "webpack -p --progress --config build/webpack.dll.config.js"
}
}
// public/index.html
<!DOCTYPE html>
<html>
<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,maximum-scale=1.0,user-scalable=no"
/>
<title>loading...</title>
</head>
<body>
<div id="app"></div>
<script src="vendor/vendor1.dll.js"></script>
<script src="vendor/vendor2.dll.js"></script>
<!-- built files will be auto injected -->
</body>
</html>
// webpack.config.js
const projectPath = process.cwd()
const webpack = require('webpack')
const config = {
// ...
plugins: [
// ...
new webpack.DllReferencePlugin({
context: projectPath,
manifest: require(path.join(projectPath, 'public/vendor/manifest.json')),
}),
// ...
],
// ...
}
打包生成 sourceMap 的时候,如果信息越详细,打包速度就会越慢。
webpack v5 开箱即带有最新版本的 terser-webpack-plugin。如果你使用的是 webpack v5 或更高版本,同时希望自定义配置,那么仍需要安装 terser-webpack-plugin。如果使用 webpack v4,则必须安装 terser-webpack-plugin v4 的版本。
使用示例(webpack@4
):
// webpack.config.js
const TerserPlugin = require('terser-webpack-plugin')
const config = {
// ...
optimization: {
minimizer: [
new TerserPlugin({
test: /\.js$/i,
parallel: os.cpus().length,
sourceMap: true,
extractComments: false,
terserOptions: {
ecma: 5,
warnings: false,
parse: {},
compress: {},
mangle: true,
keep_classnames: false,
keep_fnames: false,
output: {
beautify: false,
comments: false,
// 只用单引号
quote_style: '1',
},
},
}),
],
},
// ...
}
可忽略符合匹配条件的模块。
// webpack.config.js
const webpack = require('webpack')
const config = {
// ...
plugins: [
// ...
new webpack.IgnorePlugin({
checkResource: resourcePath => {
if (/moment\/locale\/(?!zh-cn)/.test(resourcePath)) {
return true
}
return false
},
}),
// ...
],
// ...
}
这个插件使用 cssnano 来优化和缩小你的 CSS。
// webpack.config.js
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')
const config = {
// ...
optimization: {
// ...
minimizer: [
new OptimizeCssAssetsPlugin({
assetNameRegExp: /\.(sc|le|c)ss$/,
cssProcessor: require('cssnano'),
cssProcessorPluginOptions: {
preset: ['default', { discardComments: { removeAll: true } }],
},
canPrint: true,
}),
// ...
],
// ...
},
// ...
}