你的前端项目启动速度和打包产物都该优化了吧?
前端项目启动慢?项目打包体积大?跟着陈教练一起,项目的肥油咔咔掉。
肥油咔咔掉.GIF
1. 背景
项目经过几年的迭代,项目的启动速度随着项目的迭代,肉眼可见的变慢,打包的产物也是一天比一天大。启动项目的时间、打包的时间是越来越久了。如同一个胖子,速度跟不上,体重蹭蹭涨。
接下来就跟着陈教练来两套健身操,减减脂,去去油。
2. 项目启动提速
第一套健身操 —— 提速操
所有的优化,都是需要针对问题进行优化,少数的优化是通用优化。
首先要知道,项目启动的慢,是因为什么慢。
开始前,先看一波目前我手上这个巨大项目的启动时间: before.png将近190s,3分10秒+,陈教练已经跟着刘畊宏教练跳了一首《本草纲目》了!
2.1. speed-measure-webpack-plugin
直接上工具 speed-measure-webpack-plugin, 跟着配置完后,运行项目既可看到如下的分析报告:
SpeedMeasurePlugin.png从分析报告可以看出,项目诸多 loader 占用了大半的时间,而 loader 一般又是基本不会变的,于是乎第一个针对本项目的优化方案就出来了,那就是给这些 loader 加上缓存,方法也很简单,就是使用 cache-loader 进行缓存。
npm install --save-dev cache-loader
在 webpack 配置文件内对需要缓存的 loader 进行缓存
const cacheLoader = { // 新增代码
loader: 'cache-loader' // 新增代码
} // 新增代码
module.exports = {
module: {
rules: [
{
test: /\.vue$/,
use: [
cacheLoader, // 新增代码
{
loader: 'vue-loader',
options: vueLoaderConfig
}
]
},
{
test: /\.js$/,
use: [
cacheLoader, // 新增代码
{
loader: 'babel-loader',
}
],
include: [
resolve('src'),
]
},
... ...
... ...
]
}
}
或者也可以像下面,我写的这样,在配置文件内劫持 webpack 的配置,调用 dynamicWebpackConfig 方法,批量缓存
/**
*
* @param {*} rules
* @returns rules
* @description 按需缓存需要缓存的loader
*/
function rules (rules) {
const shouldCache = ['vue-loader', 'eslint-loader', 'babel-loader']
const cacheLoader = {
loader: 'cache-loader'
}
rules = rules.map(rule => {
if (
rule.use &&
rule.use[0] &&
shouldCache.includes(rule.use[0].loader)
) {
rule.use.unshift(
cacheLoader
)
}
return rule
})
return rules
}
/**
*
* @param {*} devWebpackConfig
* @returns devWebpackConfig
* @description 完整的webpack配置
*/
function dynamicWebpackConfig (devWebpackConfig) {
devWebpackConfig.module.rules = rules(devWebpackConfig.module.rules)
return devWebpackConfig
}
module.exports = dynamicWebpackConfig
做完这一步,需要正常启动一次项目,此时会建立 loader 的缓存。缓存内容在可以在 node_modules/.cache 下看到。再次启动项目,看看启动时间:
after.png
提速了近80秒!此时不知道你是否还记得,在上面我说的这样一句话:
“所有的优化,都是需要针对问题进行优化,少数的优化是通用优化。”
针对问题的优化,已经举了一个例子说明了,那么有没有通用优化的?
通用优化,他来了
2.2. hard-source-webpack-plugin
hard-source-webpack-plugin 中间缓存,不管三七二一,只要 webpack 配置不变,通通缓存,这种这种提速方法过于简单暴力,有其他个性化的需求可以看文档进行配置
hard-source-webpack-plugin,
要注意: ip变化,端口变化也是 webpack 配置的变化
使用方法也很简单:
const hardSourceWebpackPlugin = require('hard-source-webpack-plugin')
... ...
... ...
plugins: [
new hardSourceWebpackPlugin({
cachePrune: {
maxAge: 7 * 24 * 60 * 60 * 1000, // 默认2天,现改7天删除
sizeThreshold: 500 * 1024 * 1024 // 默认50,现修改为500,目前项目启动后占用400MB的空间
}
}), //
... ...
]
同样,这些额外的配置,也可以跟我上面劫持修改 webpack 配置一样,写在一起,方便维护:
/**
*
* @param {*} plugins
* @returns plugins
* @description 按需push插件
*/
function plugins (plugins) {
const hardSourceWebpackPlugin = require('hard-source-webpack-plugin')
plugins.push(
new hardSourceWebpackPlugin({
cachePrune: {
maxAge: 7 * 24 * 60 * 60 * 1000, // 默认2天,现改7天删除
sizeThreshold: 500 * 1024 * 1024 // 默认50,现修改为500,目前项目启动后占用400MB的空间
}
}), //
)
return plugins
}
/**
*
* @param {*} rules
* @returns rules
* @description 按需缓存需要缓存的loader
*/
function rules (rules) {
const shouldCache = ['vue-loader', 'eslint-loader', 'babel-loader']
const cacheLoader = {
loader: 'cache-loader'
}
rules = rules.map(rule => {
if (
rule.use &&
rule.use[0] &&
shouldCache.includes(rule.use[0].loader)
) {
rule.use.unshift(
cacheLoader
)
}
return rule
})
return rules
}
/**
*
* @param {*} devWebpackConfig
* @returns devWebpackConfig
* @description 完整的webpack配置
*/
function dynamicWebpackConfig (devWebpackConfig) {
devWebpackConfig.plugins = plugins(devWebpackConfig.plugins)
devWebpackConfig.module.rules = rules(devWebpackConfig.module.rules)
return devWebpackConfig
}
module.exports = dynamicWebpackConfig
由于该优化方案还是缓存,所以还是需要正常启动一次项目后,建立缓存。缓存内容在可以在 node_modules/.cache 下看到。再次启动,查看启动时间:
afteragine.png
第一套健身操昨完,项目的启动速度已经从 180s 提速至 30s。
累了的同学可以先休息一会儿
3. 项目打包优化
休息结束,跟上第二套健身操 ——— 打包体积优化操
经过第一套的健身操之后,打包速度上,也会沾了缓存的光,打包速度也会有提升。但是,我们打包的体积还是那么大,怎么办?
开始前,还是需要看一看,我们打完包后的产物体积: image.png还是那句话,“所有的优化,都是需要针对问题进行优化,少数的优化是通用优化。”
那么,如何找到问题呢?
3.1 webpack-bundle-analyzer
这时候就需要用到一个很常见的工具,webpack 打包分析插件 webpack-bundle-analyzer,简单配置完后,重新运行打包命令,可以在本地的8888端口看到如下效果:
每个颜色的快,就是打包产物内的代码块,图中占面积越大的文件,其文件的大小越大,部署到服务器之后,在浏览器中加载的时间越长。我们可以根据这个图,去优化
通过这个分析图,我们可以知道:
- 了解 bundle 包中的真正内容
- 找出哪些模块尺寸最大
- 查找误引入的模块
- 优化项目
具体优化就看各位同学的眼力和优化方向了。这边我举一个例子:
譬如,我发现在 app、outer-form 两个个模块内,有一块公共的代码,分别打入这两个个模块
image.png
此时可以在 webpack 配置优化打包如下:
new webpack.optimize.CommonsChunkPlugin({
name: 'common',
chunks: ['app', 'outer-form'] // 抽取commons chunk
}),
打包后体积略有减小,因为这个地方属于略有优化。
image.png
动动眼睛动动手,其实还可以发现更多的优化点,再比如:
image.png
OK,点到为止!
4. 最后
希望大家身上的肥油也咔咔掉