背景
最近发现我的项目(基于Vue2)构建比较慢, 一次上线公布须要 15
分钟, 效率低下。
现在这个时代,工夫就是金钱,效率就是生命
。
于是这两天抽空对我的项目做了一次构建优化,线上(多国家)构建工夫, 从 10分钟
优化到 4分钟
, 本地单次构建工夫, 从 300秒
优化到 90秒
, 成果还不错。
明天把 具体的革新过程
和 相干 技术原理
整理出来分享给大家, 心愿对大家有所帮忙。
注释
首先看一下摆在面前的问题:
能够显著看出: 整体构建环节耗时过长, 效率低下,影响业务的公布和回滚
。
线上构建流程:
其中, Build base
和 Build Region
阶段存在优化空间。
Build base
阶段的优化, 和运维团队沟通过, 后续会减少缓存解决。
本次次要关注 Build Region
阶段。
初步优化后,达到成果如下:
上面介绍这次优化的细节。
我的项目优化实战
面对耗时大这个问题,首先要做耗时数据分析。
这里引入 SpeedMeasurePlugin
, 示例代码如下:
# vue.config.js
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
configureWebpack: (config) => {
config.plugins.push(new SpeedMeasurePlugin());
}
失去后果如下:
失去:
SMP ⏱ Loaders
cache-loader, and
vue-loader, and
eslint-loader took 3 mins, 39.75 secs
module count = 1894
cache-loader, and
thread-loader, and
babel-loader, and
ts-loader, and
eslint-loader took 3 mins, 35.23 secs
module count = 482
cache-loader, and
thread-loader, and
babel-loader, and
ts-loader, and
cache-loader, and
vue-loader took 3 mins, 16.98 secs
module count = 941
cache-loader, and
vue-loader, and
cache-loader, and
vue-loader took 3 mins, 9.005 secs
module count = 947
mini-css-extract-plugin, and
css-loader, and
vue-loader, and
postcss-loader, and
sass-loader, and
cache-loader, and
vue-loader took 3 mins, 5.29 secs
module count = 834
modules with no loaders took 1 min, 52.53 secs
module count = 3258
mini-css-extract-plugin, and
css-loader, and
vue-loader, and
postcss-loader, and
cache-loader, and
vue-loader took 27.29 secs
module count = 25
css-loader, and
vue-loader, and
postcss-loader, and
cache-loader, and
vue-loader took 27.13 secs
module count = 25
file-loader took 12.049 secs
module count = 30
cache-loader, and
thread-loader, and
babel-loader took 11.62 secs
module count = 30
url-loader took 11.51 secs
module count = 70
mini-css-extract-plugin, and
css-loader, and
postcss-loader took 9.66 secs
module count = 8
cache-loader, and
thread-loader, and
babel-loader, and
ts-loader took 7.56 secs
module count = 3
css-loader, and
// ...
Build complete.
fetch translations
en has been saved!
id has been saved!
sp-MX has been saved!
vi has been saved!
zh-TW has been saved!
zh-CN has been saved!
th has been saved!
$ node ./script/copy-static-asset.js
✨ Done in 289.96s.
统计出耗时比拟大的几个loader:
Vue-loader
eslint-loader
babel-loader
Ts-loader,
Thread-loader,
cache-loader
一般而言, 代码编译工夫和代码规模
正相干。
依据以往优化教训,代码动态查看
可能会占据比拟多工夫,眼光锁定在 eslint-loader
上。
在生产构建阶段, eslint 提示信息价值不大, 思考在 build 阶段去除,步骤前置
。
比方在 commit
的时候做查看, 或者在 merge
的时候加一条流水线,专门做动态查看。
给出局部示例代码:
image: harbor.shopeemobile.com/shopee/nodejs-base:16
stages:
- ci
ci_job:
stage: ci
allow_failure: false
only:
- merge_requests
script:
- npm i -g pnpm
- pnpm pre-build && pnpm lint && pnpm test
cache:
paths:
- node_modules
key: project
于此,初步确定两个优化方向:
优化构建流程
, 在生产构建阶段去除不必要的查看。集成 esbuild
, 放慢底层构建速度。
1. 优化构建流程
查看我的项目的配置发现:
# vue.config.js
lintOnSave: true,
批改为:
# vue.config.js
lintOnSave: process.env.NODE_ENV !== 'production',
即: 生产环境的构建不做 lint 查看。
Vue 官网对此也有相干形容:https://cli.vuejs.org/zh/conf…
再次构建, 失去如下数据:
SMP ⏱ Loaders
cache-loader, and
vue-loader took 1 min, 34.33 secs
module count = 2841
cache-loader, and
thread-loader, and
babel-loader, and
ts-loader took 1 min, 33.56 secs
module count = 485
vue-loader, and
cache-loader, and
thread-loader, and
babel-loader, and
ts-loader, and
cache-loader, and
vue-loader took 1 min, 31.41 secs
module count = 1882
vue-loader, and
mini-css-extract-plugin, and
css-loader, and
postcss-loader, and
sass-loader, and
cache-loader, and
vue-loader took 1 min, 29.55 secs
module count = 1668
css-loader, and
vue-loader, and
postcss-loader, and
sass-loader, and
cache-loader, and
vue-loader took 1 min, 27.75 secs
module count = 834
modules with no loaders took 59.89 secs
module count = 3258
...
Build complete.
fetch translations
vi has been saved!
zh-TW has been saved!
en has been saved!
th has been saved!
sp-MX has been saved!
zh-CN has been saved!
id has been saved!
$ node ./script/copy-static-asset.js
✨ Done in 160.67s.
有肯定晋升,其余 loader 耗时数据无显著异样。
上面开始集成 esbuid。
集成 esbuild
这部分的工作,次要是:集成 esbuild 插件到脚手架中
。
具体代码的批改,要看具体情况,大体分为两类:
- 本人用 webpack 实现了打包逻辑。
- 用的是 cli 自带的打包配置, 比方 vue-cli。
这两种形式我都会介绍,尽管模式上有所差别
, 然而原理都是一样的
。
外围思路如下:
rules: [
{
test: /\.(js|jsx|ts|tsx)$/,
loader: 'esbuild-loader',
options: {
charset: 'utf8',
loader: 'tsx',
target: 'es2015',
tsconfigRaw: require('../../tsconfig.json'),
},
exclude: /node_modules/,
},
...
]
const { ESBuildMinifyPlugin } = require('esbuild-loader');
optimization: {
minimizer: [
new ESBuildMinifyPlugin({
target: 'es2015',
css: true,
}),
],
...
}
具体实现上,简略辨别为两类, 具体配置如下:
一、webpack.config.js
npm i -D esbuild-loader
1. Javascript & JSX transpilation (eg. Babel)
In webpack.config.js:
module.exports = {
module: {
rules: [
- {
- test: /\.js$/,
- use: 'babel-loader',
- },
+ {
+ test: /\.js$/,
+ loader: 'esbuild-loader',
+ options: {
+ loader: 'jsx', // Remove this if you're not using JSX
+ target: 'es2015' // Syntax to compile to (see options below for possible values)
+ }
+ },
...
],
},
}
2. TypeScript & TSX
In webpack.config.js:
module.exports = {
module: {
rules: [
- {
- test: /\.tsx?$/,
- use: 'ts-loader'
- },
+ {
+ test: /\.tsx?$/,
+ loader: 'esbuild-loader',
+ options: {
+ loader: 'tsx', // Or 'ts' if you don't need tsx
+ target: 'es2015',
+ tsconfigRaw: require('./tsconfig.json'), // If you have a tsconfig.json file, esbuild-loader will automatically detect it.
+ }
+ },
...
]
},
}
3. JS Minification (eg. Terser)
esbuild 在代码压缩上,也有不错的体现:
具体比照数据见:https://github.com/privatenum…
In webpack.config.js:
+ const { ESBuildMinifyPlugin } = require('esbuild-loader')
module.exports = {
...,
+ optimization: {
+ minimizer: [
+ new ESBuildMinifyPlugin({
+ target: 'es2015' // Syntax to compile to (see options below for possible values)
+ css: true // Apply minification to CSS assets
+ })
+ ]
+ },
}
4. CSS in JS
如果你的 css 款式不导出为 css 文件, 而是通过比方’style-loader’加载的,也能够通过esbuild来优化。
In webpack.config.js:
module.exports = {
module: {
rules: [
{
test: /\.css$/i,
use: [
'style-loader',
'css-loader',
+ {
+ loader: 'esbuild-loader',
+ options: {
+ loader: 'css',
+ minify: true
+ }
+ }
]
}
]
}
}
更多 esbuild 案例, 能够参考: https://github.com/privatenum…
二、vue.config.js
配置比较简单,间接贴代码了:
// vue.config.js
const { ESBuildMinifyPlugin } = require('esbuild-loader');
module.exports = {
// ...
chainWebpack: (config) => {
// 应用 esbuild 编译 js 文件
const rule = config.module.rule('js');
// 清理自带的 babel-loader
rule.uses.clear();
// 增加 esbuild-loader
rule
.use('esbuild-loader')
.loader('esbuild-loader')
.options({
loader: 'ts', // 如果应用了 ts, 或者 vue 的 class 装璜器,则须要加上这个 option 配置, 否则会报错: ERROR: Unexpected "@"
target: 'es2015',
tsconfigRaw: require('./tsconfig.json')
})
// 删除底层 terser, 换用 esbuild-minimize-plugin
config.optimization.minimizers.delete('terser');
// 应用 esbuild 优化 css 压缩
config.optimization
.minimizer('esbuild')
.use(ESBuildMinifyPlugin, [{ minify: true, css: true }]);
}
}
这一番组合拳打完,本地单次构建:
成果还是比拟显著的。
一次线上构建, 整体工夫从 10 分钟缩短为 4 分钟。
开心不到两分钟,发现隔壁我的项目居然能够做到 2 分钟…,
这我就不服气了,同样是 esbuild , 为何你的就这么秀?
去钻研了一下, 找到了起因。
- 他们的我的项目是 React + TSX, 我这次优化的我的项目是 Vue, 在文件的解决上就须要多过一层
vue-loader
。 - 他们的我的项目采纳了微前端, 对我的项目对了拆分,主我的项目只须要加载基座相干的代码, 子利用各自构建。 须要构建的主利用代码量大大减少, 这是次要起因。
这种微前端的拆分形式在我之前的文章中提到过, 看趣味的能够去看看。
你须要理解的 esbuild
第一局部次要介绍了一些实际中的细节, 根本都是配置, 没有太多有深度的内容, 这部分将介绍 更多 esbuild 原理性的内容作为补充。
结语
esbuild 是一个弱小的工具,心愿大家能充沛应用起来, 为业务带来更大价值。
好了,明天的内容就这么多,心愿对大家有所启发。
满腹经纶,文章若有谬误,欢送留言指出。
参考资料
https://cli.vuejs.org/zh/conf…
https://esbuild.github.io/get…
https://morioh.com/p/cfd2609d…
https://battlehawk233.cn/post…
https://esbuild.github.io/api…
https://webpack.docschina.org…
https://github.com/privatenum…
发表回复