一个前端项目的 webpack 调优记录
前言
公司的一个核心前端项目是使用 vue-cli 脚手架生成的,用得较为老旧的技术栈(Vue2 + Webpack4),项目启动和打包非常慢,组内的前端同学已经无力吐槽,亟待升级优化。而最新的 webpack5 发布至今已经 2 年多了,刚好项目需求开发完有空余时间,便决定给项目来一次 webpack 调优改造,提升前端开发效率。
思路
思路很简单,我们先将核心依赖升级至最新版(v4 升级至 v5),对原 plugin、loader 进行按需替换及移除,按 development
、production
环境进行测试,解决升级过程中遇到的各种写法和兼容性问题。
项目旧依赖如下:
"webpack": "^4.20.2",
"webpack-bundle-analyzer": "^3.8.0",
"webpack-dev-middleware": "^3.4.0",
"webpack-hot-middleware": "^2.24.3",
"webpack-merge": "^4.1.4",
依赖升级
- 分别安装以下依赖:
webpack-cli
、webpack
、webpack-merge
;
npm install webpack-cli webpack@latest webpack-merge@5 --save-dev
- 尝试打包
npm run build -- view=demo loginbg=szw loginicon=qhdata
webpack-merge
升级到版本 5,merge
方法需要解构使用,涉及 webpack-merge
的地方统一修改导入方式;
const { merge } = require('webpack-merge')
启动
- 浏览器控制台报错:Uncaught ReferenceError: process is not defined
webpack.dev.conf.js
添加 DefinePlugin
设置
new webpack.DefinePlugin({ 'process.env': config.dev.env }),
官方迁移文档提及到这部分:
打包
- TypeError: webpack.HashedModuleIdsPlugin is not a constructor
移除 HashedModuleIdsPlugin
插件。
// new webpack.HashedModuleIdsPlugin()
- 大段的报错信息,二分定位错误。项目的
webpack
配置生成是通过webpack-merge
的方式生成,所以可先将baseWebpackConfig
或prodWebpackConfig
置为空对象,定位错误的配置项,缩小排查范围。
分析 baseWebpackConfig 配置
,需要对比 v4
和 v5
版本的配置项差异。
module.exports = {
entry: {},
output: {},
node: {},
externals: {},
resolve: {},
module: {},
optimization: {},
plugins: []
}
v4 版本
module.exports = {
node: {
dns: 'mock',
fs: 'empty',
path: true,
url: false
},
}
v5 版本
module.exports = {
node: {
global: false,
__filename: false,
__dirname: false,
},
};
v4
关于 node
的设置项在 v5
版本的 webpack core
解析会出错。去除 node
配置项后重新 build
,报错信息不再显示。
- TypeError: compiler.plugin is not a function
optimize-css-assets-webpack-plugin
插件版本太低,直接移除。
npm uninstall optimize-css-assets-webpack-plugin --save-dev
- Error: Cannot find module 'webpack/lib/RuleSet'
v5
版本内置了 inline
资源模块,不需要使用 svg-sprite-loader
处理 svg 文件,修改 svg
文件规则。
修改前:
{
test: /\.svg$/,
include: [resolve('src/icons')],
use: {
loader: 'svg-sprite-loader',
options: {
symbolId: 'icon-[name]',
},
},
},
修改后:
{
test: /\.svg/,
type: 'asset/inline'
}
resolve.fallback
,指定解析失败后,是否重定向模块请求,按提示添加即可。
修改前:
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
vue$: 'vue/dist/vue.esm.js',
'@': resolve('src'),
},
},
修改后:
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
vue$: 'vue/dist/vue.esm.js',
'@': resolve('src'),
},
fallback: {
fs: false,
path: false,
crypto: false,
url: false,
},
},
babel
默认处理文件大小设置
修改 .babelrc
文件,添加 env
配置项:
"env": {
"development": { "compact": false },
"production": { "compact": false }
}
- [DEP_WEBPACK_CHUNK_MODULES_ITERABLE] DeprecationWarning: Chunk.modulesIterable: Use new ChunkGraph API
升级 mini-css-extract-plugin
插件:
npm install mini-css-extract-plugin@latest --save-dev
- [DEP_WEBPACK_COMPILATION_ASSETS] DeprecationWarning: Compilation.assets will be frozen in future, all modifications are deprecated.
升级 copy-webpack-plugin
插件:
npm install copy-webpack-plugin@latest --save-dev
优化
项目从启动到打包都存在着以下问题:
- 启动、打包慢,平均耗时 3 分钟以上;
- 冗余的控制台 log 非常多。
我们分别引入一些插件来解决下面的问题:
speed-measure-webpack-plugin
用于分析总打包耗时及每个 plugin、loader 的耗时情况,后续可用于定向优化。
在 webpack.dev.conf.js
中添加 speed-measure-webpack-plugin
处理:
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
const devWebpackConfig = merge(baseWebpackConfig, {
mode: 'development',
module: {},
devtool: 'eval',
plugins: [
new webpack.DefinePlugin({ 'process.env': config.dev.env }),
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html',
inject: true,
}),
new FriendlyErrorsPlugin(),
],
})
/**
* speed-measure-webpack-plugin 与 vue-loader-plugin 插件不兼容
* 不兼容的插件在 wrap 之后再 push plugins
*/
const smp = new SpeedMeasurePlugin()
const smpWrapWebpackConfig = smp.wrap(devWebpackConfig)
smpWrapWebpackConfig.plugins.push(new VueLoaderPlugin())
smpWrapWebpackConfig.plugins.push(new webpack.HotModuleReplacementPlugin())
module.exports = smpWrapWebpackConfig
项目的耗时情况如下:
可以看到,vue-loader
耗时占用最高,这是因为项目存在非常多的 .vue
文件(数量多达 1693 个)。
progress-bar-webpack-plugin
一个可用于查看当前的任务进度插件,在 webpack.base.conf.js
中添加配置:
const ProgressBarPlugin = require('progress-bar-webpack-plugin')
module.exports = {
...
plugins: [
new ProgressBarPlugin({ format: `xxxx-任务极速处理中: :msg [:bar] ${chalk.green.bold(':percent')} (:elapsed s)` }),
],
}
loader
写法简化
未添加配置项的,直接使用名称即可,看起来更简洁。
修改前:
{
test: /\.less$/,
use: [
{
loader: 'thread-loader',
},
{
loader: 'vue-style-loader',
},
{
loader: 'css-loader',
},
{
loader: 'less-loader',
},
],
},
修改后:
{
test: /\.less$/,
use: ['thread-loader', 'vue-style-loader', 'css-loader', 'less-loader'],
},
控制台输出
移除间隔 10 秒输出及其他冗余的控制台输出信息
// if (!flage.view) {
// console.log(chalk.red(' ️!!!警告!!!'))
// console.log(chalk.red(' ️你没有指定view参数'))
// }
// setInterval(function () {
// logRunningInfo()
// }, 10000)
引入项目信息,基于 chalk
进行自定义输出。
文件缓存
性能提升 90% 的配置,在 webpack.base.conf.js
中添加以下内容:
cache: { type: 'filesystem' }
分别进行若干次打包测试:
优化前:
打包次数 | 打包时间 | 启动时间 |
---|---|---|
第一次 | 00:03:05 | 153.399s |
第二次 | 00:02:57 | 129.427s |
第三次 | 00:03:06 | 126.139s |
平均打包耗时:00:03:02,平均启动耗时:136.321s
优化后:
打包次数 | 打包时间 | 启动时间 |
---|---|---|
第一次 | 00:03:19 | 128.76s |
第二次 | 00:00:12 | 9.816s |
第三次 | 00:00:13 | 10.298s |
平均打包耗时:00:01:14,平均启动耗时:49.625s
jenkins
打包测试
优化前:
优化后:
单次构建对比,启动和打包效率提升 10 倍!
PS:文件缓存结果存储在 node_module 文件夹下,会占用约 2 个G的磁盘空间(取决于项目规模)。
总结
webpack v4 升级至 v5 后,可以使用 v5 更多更强大的新特性,为项目的后续优化提供了基础。在当前未做更多优化的情况下,仅靠文件缓存一项配置,就给项目启动和构建带来了足够大的效率提升。
转载自:https://juejin.cn/post/7220383202485452860