背景
最近入职的公司是一家TO B公司, 前后端分离,几乎所有的业务逻辑都是后端处理,前端基本上就是一个页面仔,和后端确认好字段后就可以开始单机开发。
项目维护多年,基建有很多地方都可以优化
- 项目开发/生产环境优化
- 前端项目持续集成
- 公司私有组件库
- 性能优化等等
从这里起,会持续记录优化的过程,今天先来看看项目webpack配置的更新。
开发环境与生产环境
修改webpack前,首先要明确的是作为一个前端工程师,工作的大部分时间都是在开发环境中度过,速度对开发工作的影响至关重要,而在生产环境中,项目的性能,文件打包构建速度,文件体积及优化才是重点。在开发环境中过分追求包体积的优化或者首屏加载速度,实在是没有必要。
本轮优化主要分为两个部分,开发环境以及生产环境。这一节,就先来优化开发效率。
webpack配置更新
公司的项目虽然是用了webpack5,依然是沿用了webpack3的配置,由于webpack5已经集成很多以前需要通过loader或plugin来实现的功能,如编译缓存,file-loader等等,这些在webpack5中已经集成,所以可以直接使用webpack5的配置。
另外一个方面,前端是一个更新迭代非常快的行业,webpack3的loader或plugin肯定有很多东西已经被废弃或有更优的选择,如对标babel的swc,使用swc-loader可以编译速度提升数倍甚至更多。
构建性能分析
要了解构建性能的优化对比,首先给项目加入构建性能分析工具,这里选择speed-measure-webpack-plugin
https://www.npmjs.com/package/speed-measure-webpack-plugin
在webpack配置中,几乎都会有多环境配置,每个配置都需要单独使用该插件包裹
-
speedMeasure.js
const SpeedMeasurePlugin = require(“speed-measure-webpack-plugin”);
const paths = require(“./paths”);
const smp = new SpeedMeasurePlugin({
// your config
});module.exports = smp;
common.config.js
const { merge } = require("webpack-merge");
const smp = require("./utils/speedMeasure");
module.exports = smp.wrap(
merge(
// common config
)
);
dev.config.js
const { merge } = require("webpack-merge");
const smp = require("./utils/speedMeasure");
module.exports = smp.wrap(
merge(
// dev config
)
);
加入该组件后,即可以看到编译时间以及loader的大小
除了直接在terminal上面显示,也可以通过配置输出文件记录到日志文件上面, 可以按需配置。
图片与字体文件的处理
file-loader
在webpack5以前,如果webpack需要对图片,字体灯文件进行处理,都需要使用到file-loader,如下
{
test: /\.(eot|woff|woff2|ttf)(\?.*)?$/,
use: [{
loader: require.resolve('file-loader'),
options: {
name: `static/[name].[hash:8].[ext]`,
esModule: false,
},
}],
},
type
https://webpack.js.org/guides/asset-management/#loading-fonts
而在webpack5中,file-loader的功能已经内置可以直接通过指定文件的type让webpack进行处理
{
test: /\.(png|jpe?g|gif|webp|ico|svg)(\?.*)?$/,
type: 'asset/resource',
},
{
test: /\.(eot|woff|woff2|ttf)(\?.*)?$/,
type: 'asset/resource',
},
构建性能提速
像这种To B项目,交付按期完成远比技术升级迭代的需求要重要得多,所以对项目构建性能的重视程度很低。记得在呆过的一家业务流程差不多的公司,用一台8g的window,没保存一次平均等待20s,工作效率极低。虽然现在的项目等待时间只有几秒,但是人生苦短,编译的速度当然是越快越好。
使用swc替换babel
babel-loader
作为一名前端, babel对我们来说肯定不陌生,通过babel-loader我们可以对js,jsx,ts文件进行处理,转换成为当前或旧版本浏览器向后兼容的js代码。babel是使用js便携的一个代码转换工具,虽然功能强大,但局限于js本身代码的执行效率,在项目越来越大的情况下,babel所需要的时间也较长。
{
test: /\.ts|tsx$/,
use: [
{
loader: require.resolve('babel-loader'),
options: {
// options
},
},
{
loader: require.resolve('ts-loader'),
options: {
// options
},
},
],
},
swc–对标babel
swc是使用rust编写的一个可拓展平台, 可以用于编译以及打包js代码,next.js,parcel和deno等工具都在使用.
-
根据官网配置最基本的swc-loader
{
test: /.[j|t]sx?$/,
exclude: /node_modules/,
use: {
loader: “swc-loader”
}
}, -
.swcrc
{
“jsc”: {
“parser”: {
“syntax”: “typescript”,
“tsx”: true,
“decorators”: true
},
“loose”: true
}
}
官方模式配置是只对js做处理,加入项目中使用到了ts,react,则需要加上对应的配置,从需要处理的文件到对浏览器支持的配置,都与babel非常相似,迁移成本较小,看这熟悉的浏览器支持版本配置
{
"env": {
"targets": {
"chrome": "79"
},
"mode": "entry",
"coreJs": "3.22"
}
}
启动项目
根据分析插件的输出, 在其他配置相同的情况下, babel-loader需要时间为14s, 而swc-loader只需要2.5s, swc-loader的编译速度是babel-loader的6倍
文件修改
修改文件保存,可以看到swc的速度是babel的大概50倍
为什么速度相差这么多?
因为babel是使用js进行开发, 由于js是单线程,对繁重计算的处理并不是非常。而swc使用rust开发,在多线程中运行,而不是在js的事件循环中执行。
在官网上介绍SWC is 20x faster than Babel on a single thread and 70x faster on four cores.
在单核模式上会比babel快20倍,四核模式上快70倍,而笔者正在使用的是10核,实际情况并没有达到上面所说的效果。可能在更大型的项目中会有更加明显的效果。
开启编译缓存
明明只是加了个console,却把项目上所有需要编译的文件都编译了一次,相信是所有前端都感觉迷惑的事情,特别是维护那种大型老旧项目时特别有感觉。通过开启编译缓存 ,可以把编译结果缓存起来,文件启动以及修改某文件的编译速度会得到明显提升
在webpack5以前我们可以通过一些loader/plugin来做编译缓存,如:cache-loader,hard-source-webpack-plugin,而在webpack5中可以直接配置cache来设置编译缓存
用以下配置后,webpack会在本地生产缓存文件
cache: {
// 保存到文件中
type: "filesystem",
// 运行内存收集
allowCollectingMemory: true,
},
开启前
开启后
启动速度
首次启动,启动的总时间与未开启几乎相同
而启动成功后,可以在node_modules中看到webpack生成了缓存文件
在生成cache再次启动项目,启动速度只需要1s,速度大概快了5倍
文件修改,编译速度提升
随便修改一个文件,编译时间只需要0.8s
总结
本次优化是针对开发环境的速度提升,启动速度提升了数倍,编译时间降低到了1s以内,下一周开始搞生产模式的性能优化。
最后贴上和自己研究了一天猫猫
暂无评论内容