1、背景
当前网站项目采用的是多页面,开发和重构分离,开发新页面时,因为重构开发可能经常会添加样式文件或者调整样式顺序,因此小组采用了如下开发模式
随着页面越来越多,依赖的样式文件变多,gulp
任务也随之变多,打包编译速度慢的问题逐渐暴露出来,这种模式下的打包时间截图如下:
可以看到gulp
任务大约需要30s
, webpack任务需要30s
,加起来第一次构建需要1分多钟,而二次构建也需要8秒左右,想象每次构建脑袋中都跳出这个场景,这能忍受?可见已经到了必(ren)须(wu)要(ke)改(ren)的地步了好吗
于是决定修改当前构建方式,分为以下步骤进行
1、去掉gulp
修改后的模式如下图,其中绿色为新增流程,和原流程相比去掉了gulp
打包css
流程转而也交给webpack
处理
切换到这种模式后,第一次打包时间大约20s,增量编译大概8s
2、webpack
构建速度优化
2.1 exclude include
优化
在匹配规则里指定需要处理和排除在外不处理的文件,缩小查找和处理文件范围,确保编译尽可能少的文件,针对项目情况添加对应的include
和exclude
配置,具体配置如下
rules: [
{
test: /\.[jt]s$/,
include: [path.resolve(process.cwd(), 'src')],
exclude: [/node_modules/, path.resolve(process.cwd(), 'src/vendor')],
},
{
test: /\.css$/,
include: RESOURCES.CSS.cwd,
}
]
加上上面配置后,第一次打包时间大约16s,增量编译大概8s
2.2 thread-loader
和esbuild-loader
优化
thread-loader
是采用多进程打包的方式,放在thread-loader
后面的loader
会单独的worker
池里运行,esbuild
是由Go
开发的用于打包压缩ts
、js
的工具,特点是打包速速很快,官方github
介绍与webpack
, rollup
, Parcel
相比较要快几十甚至上百倍,但是esbuild目前还不能支持css, 并且没有插件机制,所以目前暂时替代不了webpack,但是有用于webpack
中的loader
即esbuild-loader
, 引入一下试一下效果, Vite
和Snowpack
底层都采用了esbuild
,参考thread-loader
官方说明和esbuild-loader
官方说明后关键配置如下
const cpuNum = require('os').cpus().length;
const tsWorkerPool = {
workers: 6,
poolTimeout: Infinity
};
const cssWorkerPool = {
workers: cpuNum - tsWorkerPool.workers,
poolTimeout: Infinity
};
threadLoader.warmup(tsWorkerPool, ['esbuild-loader']);
threadLoader.warmup(cssWorkerPool, ['css-loader']);
module: {
rules: [
{
test: /\.[jt]s$/,
use: [
{
loader: 'thread-loader',
options: tsWorkerPool
},
{
loader: 'esbuild-loader',
options: {
loader: 'ts',
target: 'es2015',
tsconfigRaw: require('../tsconfig.json')
},
},
{
loader: path.resolve(__dirname, 'loaders/importcss-loader.js'),
options: {
include: path.resolve(process.cwd(), 'src/app'),
}
},
],
include: [path.resolve(process.cwd(), 'src')],
exclude: [/node_modules/, path.resolve(process.cwd(), 'src/vendor')],
},
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
{
loader: 'thread-loader',
options: cssWorkerPool
},
{
loader: 'css-loader',
options: {
esModule: false,
}
},
{
loader: 'esbuild-loader',
options: {
loader: 'css',
minify: true
}
}
],
include: RESOURCES.CSS.cwd,
}
],
},
经过测试,加上thread-loader
后,第一次打包时间大约12s
,增量编译大概3s
,再加上esbuild-loader
后,第一次打包时间缩短到8s
左右,增量编译大概2s
注意: https://esbuild.github.io/api/#target
文档中提到针对大部分语法esbuild-loader
只支持转换到es6
,因此只适合在开发环境使用,生产环境不建议使用
2.3 图片压缩和去掉冗余css样式文件
图片压缩配置如下
loader: 'image-webpack-loader',
options: {
// 生产环境启用压缩
disable: process.env.NODE_ENV === 'production' ? false : true,
// 压缩 jpg/jpeg 图片
mozjpeg: {
progressive: true,
quality: 80 // 压缩率
},
// 压缩 png 图片
pngquant: {
quality: [0.65, 0.90],
speed: 4
}
}
去掉冗余样式使用purgecss插件,配置如下, 需要针对特殊的不能去除的加上配置
new PurgecssPlugin({
paths: glob.sync([
path.resolve(__dirname, '../../server/views/**/*.html'),
path.resolve(`${PATH.SRC}/**/*.vue`),
]),
safelist: [
/data-v-.*/, // vue scope样式保留
/market-message/, // 处理营销通知栏背景颜色由配置决定导致需要剔除样式不确定的特殊处理
/vip/, // 动态渲染vip等级样式保留
]
})
3、升级到webpack5
webpack
去年发布了webpack5
带来了很多优化,主要几点比如:
1、默认开启持久化缓存并缓存在内存中,而webpack4
则需要借助cache-loader
和hard-source-webpack-plugin
来做缓存
2、NodeJS
的polyfill
脚本被移除, 而在webpack4
及以前的版本中,对于大多数的Node
模块将自动添加polyfill
脚本,导致打包后体积会变大。
3、更好的TreeShaking
经过上面优化后,想进一步测试一下webpack5
版本能带来的优化效果,将webpack
包和相关依赖升级到最新版本,去掉不兼容的speed-measure-webpack-plugin
和已经不需要的hard-source-webpack-plugin
插件后,处理大概65个文件,第一次打包时间缩短到6.5s
左右,增量编译时间1.5s
左右,可以看到时间进一步缩短。
4、热加载和热更新
webpack.dev.js
中添加以下配置
devServer: {
contentBase: path.join(PATH.DIST, 'js'),
inline: true,
compress: true,
port: 5000,
writeToDisk: true,
host: '0.0.0.0',
hot: true,
disableHostCheck: true,
headers: {
'Access-Control-Allow-Origin': 'https://www.midasbuy.com',
'Access-Control-Allow-Headers': '*',
'Access-Control-Allow-Methods': '*',
'Access-Control-Allow-Credentials': true,
},
},
因为我们开发时是用的whistle
代理配合域名访问,在检测到文件文件更新时是通过websocket
来进行消息通知的,因此需要在whistle
中添加规则如下,第一条是建立websocket
连接,第二条用于热更新时请求更新的内容,这里主要实现了vue
组件的热更新和js
的热刷新,css热跟新暂时没实现。
https://www.midasbuy.com:5000 http://127.0.0.1:5000/
^https://www.midasbuy.com/oversea_web/static/***hot-update*** http://127.0.0.1:5000/oversea_web/static/$1hot-update$2
坑:测试热刷新时,修改了文件,页面刷新后展示如下报错
然后再次手动刷新就好了,半天没找到原因,一度还怀疑是whistle
代理的原因,之后怀疑是发送通知更新指令太早了,然后写了一个hack
方法来延迟发送更新指令,发现可以了,但是具体原因还没找到,直到切换到服务端终端,更新文件时看到了如下打印
才明白了,我们的流程是client
端编译好文件后,会将文件复制到server
文件夹下面来提供给ejs include
进来使用,server
使用了nodemon
,文件变更时会进行复制文件操作,触发了nodemon
的重启导致浏览器reload
时请求失败,所以解决办法是在nodemon
配置文件中把复制文件的目录忽略掉,因为复制的真正内容是<style link='*****'></style>
和<script src='*****'></script>
这样的内容,所以完全是可以忽略的,忽略后文件变更时服务端也不会重启,问题解决。
5、vue devtools
定位到组件目录
针对vue devtools
提供的在浏览器选择组件时,点击时可以直接打开编译器并打开组件所在源代码文件,参考文档在webpack.dev.js
下新增下面配置
const openInEditor = require('launch-editor-middleware');
devServer: {
...
before(app) {
app.use('/__open-in-editor', openInEditor('code', path.resolve(process.cwd(), 'src')));
},
},
在whistle
添加一行规则
https://www.midasbuy.com/__open-in-editor http://127.0.0.1:5000/__open-in-editor
如下图,点击圈出的该位置时会打开vscode
编辑器并定位到组件对应源文件代码
6、总结
在MacBook Pro Intel Core i7 16G
内存250G
硬盘大小测试环境下 对比不同方案下的编译时间和主要文件的gzip
压缩前的大小结果如下,在处理大约65
个文件情况下,可以看到编译时间大大缩短,js
文件大小也有所减小,但是因为部分图片被base64
编码打进css
导致样式文件大小有所增加。
方案 | 第一次编译时间 | 第二次编译时间 | buypage.js | propsOrder.js | buypage.css | propsOrder.css |
---|---|---|---|---|---|---|
gulp +webpack4 | 60s | 8s | 178kb | 157kb | 17.58kb | 24.74kb |
webpack5 | 6.5s | 1.5s | 123kb | 112kb | 28kb | 35kb |
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。