vue-cli项目升级rsbuild,效率提升50%+
项目中有上千个页面,原有的脚手架vue-cli
打包时间需要好几分钟。开发时改个文件等待编译时间长,严重影响了开发效率。
为了提升开发效率以及缩短打包时间,在vite
和rspack
中,最终选择了rspack
。rspack
是webpack的平替,它兼容webpack的插件,迁移心智负担低。目前rspack
版本是1.3
。
官网提供了基本的迁移,可以参考vue-cli 迁移
vue2 插件版本仅支持
>=2.7.0
版本,可以直接升级vue版本到最新的2.7.16
迁移之路 {#migrate-road}
迁移之路真是一波三折,在rspack
版本为0.6
的时候就尝试过了,当时测试的编译时间比vue-cli
快很多,但是当时功能可能不太完善,有些bug实在是改不动了,便搁置了。这在文章里有所体现,文章里有些问题当时记录了现在没有删除,只是记录仅供查看。
而且间隔这么长时间,之间项目开发的页面文件较之前又增加了很多,期间我没参与这个项目的开发,也就没管。现在又开始接触这个项目了,发现开发启动和打包编译时间都很长。这我不能忍😤
项目的初始配置为vue-cli@3
及相关配置,现在的vue-cli
版本最新已经是5.0.8
了。按照我电脑的性能mac
来对比编译时间,相比同事windows的电脑编译时间则会更长。
在初始项目配置vue-cli@3
配置下,我的编译时间为1m20s 多,同事电脑上则会有5m多。升级到rspack
最新版本1.3.17
,升级之路还好,遇到的问题基本记录在案了。当时满心欢喜的把配置也拉满了,心想这升级后性能提升虽不能翻倍,但编译时间提升肯定甩它个几条街。事实被打脸了😭
升级完之后我电脑的平均编译时间为3m30s. 我整个人都不好了,为什么啊?😳 还好当时开发环境有配置按需编译,这一点让我没有立马扔掉rspack
进行回退。
export default defineConfig({dev: {lazyCompilation: true},
})
然后就是各种翻阅文档,查看各种优化手段,尝试并减配,但编译时间仍不能提升上来,抱着不放弃的态度,在dicussion
提了问题,当然也没抱什么希望,觉得短时间也不会有人回复,如果第二天不能得到解决,我就回退了,像是下定了某种决心🙃。
没想到得到了大佬的回复,然后根据大佬的提示进行性能分析https://rsbuild.dev/zh/guide/debug/build-profiling , 得知了rspack
编译时rust
侧时间消耗在2m左右,因为这段在时序图里时空白的,所以看不到什么。其它时间消耗在vue-loader
上,对于js侧消耗时间其实能接受,减掉这2分钟,编译时间和之前差不多持平。
于此同时我在rstack
交流群里也发布了同样的问题,看大家是否遇到同样的问题,群里大佬也回复让我使用性能分析,使用了rsdoctor
工具分析时遇到功能无法使用,大佬说我的项目文件太大了,他们会继续优化该问题,到此问题并没有得到解决,只能等到第二天准备回退到vue-cli
了。
为了保留升级后的配置信息,拉了新的分支,以便后续问题解决后再升级。并准备升级vue-cli
到最新版本5.0.8
,想的是怎么着升级之后性能虽不能有大的提升,但也应该有所提升吧。现实又是狠狠的打了我一巴掌😣
在所有的依赖升级完成之后,满心欢喜的开始编译构建,随着等待时间,心一下子凉了。怎么个事啊,MLBZD 😤 编译时间足足翻了一倍3m40s,平均都快到5m多,这还是我的电脑上,那同事电脑上岂不是炸了。
没事没事,谁让我爱折腾呢,只能心里安慰了,然后反复在vue-cli@3
和vue-cli@5
测试了多次编译时间后,我明白了升级并不一定能带来编译时间的提升,升级之后还带来了依赖包尺寸增大的问题,功能多了编译时间自然也就长了。
在我快要放弃了的时候(心想搞这升级干嘛,吃饱了撑的),此时群里有大佬回复了,我仿佛看到了曙光,他说可以尝试rsbuild@1.2
版本,他们在升级之后速度是有提升的,但是升级rspack@1.3
版本后会有降速的问题,所以建议不要使用版本1.3
.
Ok,我这人听劝,迫不及待的就降级版本到1.2.19
,并且阅读了版本1.3
的发布公告,发现对我的配置并没有影响,配置优化暂时可以不在乎,可以直接使用。调整完之后,怀着忐忑的心执行npm run build
,期待奇迹的发生,果然编译速度提升到了一个不可置信的高度,重新编译计时 50s,至此悬着的心终于放心了,给大佬点赞,给rsbuild
点赞,给rspack
点赞.
让同事测试了编译时间为2m20s左右,比之前的编译时间缩短了一半。
升级之后不仅得到编译速度的提升,对于一些依赖也升级到了最新版本,带来了新功能,更能助力我们开发,提升开发效率。
好了,下面是vue-cli
向rsbuild
升级需要修改的配置。安心享用吧😉
迁移配置
移除vue-cli
依赖,安装rsbuild
的依赖
移除vue-cli
相关依赖@vue/*
:
npm remove @vue/cli-service @vue/cli-plugin-babel @vue/cli-plugin-eslint core-js
可以先移除与
babel
相关的所有依赖,后续再根据错误信息步步添加配置。
可以顺手把其他功能依赖版本升级到minor
版本最新。如果你对其它依赖主版本比较了解,则可以升级到最新版本。
安装rsbuild
的依赖,并且需要支持vue / jsx
,安装相关依赖,我们的项目是vue2的
npm add @rsbuild/core @rsbuild/plugin-vue2 @rsbuild/plugin-vue2-jsx @rsbuild/plugin-babel -D
删除掉原来的配置文件vue.config.js
,新建rsbuild
配置文件rsbuild.config.js
。
-
支持
jsx
,需要安装@rsbuild/plugin-vue2-jsx @rsbuild/plugin-babel
-
html.template
配置模板,指向./public/index.html
-
增加
source.alias
配置路径别名,对应的是rspack
中的resolve.alias
-
修改
package.json
中的脚本部分,移除掉vue-cli-service
该用rsbuild
{"scripts": {"dev": "rsbuild dev","build": "rsbuild build","lint": "eslint --ext .js,.jsx,.vue ./src"} }
rsbuil 不包含
lint
,所以直接使用eslint
基本的迁移配置rsbuild.config.js
import { defineConfig } from "@rsbuild/core";
import { pluginVue2 } from "@rsbuild/plugin-vue2";
import { pluginBabel } from "@rsbuild/plugin-babel";
import { pluginVue2Jsx } from "@rsbuild/plugin-vue2-jsx";
const path = require("path");export default defineConfig({plugins: [pluginVue2(),pluginBabel({include: /\.(?:js|jsx|tsx)$/,exclude: /[\\/]node_modules[\\/]/,}),pluginVue2Jsx(),],html: {template: "./public/index.html",},source: {// 指定入口文件entry: {index: "./src/main.js",},alias: {"@": path.resolve(__dirname, "./src"),},},
});
html
模板中的变量<%= BASE_URL %>
变更为<%= assetPrefix %>/
- <link rel="icon" href="<%= BASE_URL %>logo.png" />+ <link rel="icon" href="<%= assetPrefix %>/logo.png" />
less\sass
通过插件配置实现, 移除掉了已安装的依赖
npm remove less less-loader sass sass-loader
安装相关插件,并在配置rsbuild.config.js
中使用插件。
npm add @rsbuild/plugin-less @rsbuild/plugin-sass -D
在迁移之路 中我们使用了
rsbuild
的低版本1.2
,所以这里less \ sass
的版本我们也降级。@rsbuild/plugin-less@1.1.1
和@rsbuild/plugin-sass@1.2.2
插件包含的less、sass版本默认是最新的,如果存在深度依赖某个版本的功能,可以安装指定版本并通过配置插件配置项,
export default defineConfig({plugins: [pluginLess({lessLoaderOptions: {implementation: require("less"),},}),]
})
安装指定的less版本,对于sass也是一样的,可指定版本安装。
npm add less@3 -D
vue文件里的scoped
样式穿透时使用的/deep/
不生效了,但是查看了vue插件vue-loader
没说不支持,修改为::v-deep
就可以了。
问了社区为什么不支持,/deep/
废弃了,它不是标准的css语法。https://github.com/rspack-contrib/rsbuild-plugin-vue2?tab=readme-ov-file#faq
引用静态资源,比如图片 (这个问题已经不存在了)
除了相对目录引入之外../../assets/test.png
,可以通过路径别名@
来访问,也就是@/assets/test.png
遇到一个问题就是如果一个 less 中引入资源图片,在另一个 less 文件中导入这个文件,就会出现图片资源加载不到的问题
@import "../../base.less";// ...
这样就会导致base.less
中的图片引入地址~@/assets/test.png
变为../../~@/assets/test.png
导致加载不到。感觉这应该是一个 bug 🤔。
以~
开头是 vue-cli 的资源转换规则,它会将这个内容作为一个模块请求解析。~@
则会请求获取路径别名@
下指定的资源。
为了批量处理,写一个解析 loader 将~@
改为@
。
一个解决方案就是不要在样式文件中导入另一个样式文件,可以在 js 文件中导入 less 文件。
还有 less
文件变量的问题,可以将全局的变量声明放到一个 less 文件variables.less
. 配置为全局引入。
这个修改的工作量还是很大的 😢 需要修改所有引用变量文件的地方。
修改配置,增加 less 配置,全局注入变量
export default defineConfig({// ...tools: {less: {additionalData: '@import "@/variables.less";',},},
});
内置了postcss
,内置了autoprefixer
移除掉本地依赖,可以通过tools.postcss
进行配置,当然也可以通过外部配置文件postcss.config.js
自定义配置;
npm remove postcss autoprefixer
这个是在rsbuild
版本为0.6
时的问题,现在新版本没有问题了。仅供查看
rsbuild 内置的 postcss 版本是最新的v8
,通过postcss.config.js
配置的插件编译不能通过
对于引入的插件 postcss 插件postcss-px-to-viewport
构建错误,提示不存在方法,就去查看了插件有没有更新。发现好久没更新了,就按照错误提示看看插件怎么更新
Why do I need to update my plugin?
还好,需要改动的地方还算清晰,直接上手修改了。在node_modules
找到这个包找到入口文件,修改文件
- 把
postcss
移到 peerDependencies 里面 - 使用
module.exports = creator
代替module.exports = postcss.plugin(name,creator)
- 原来返回一个函数,现在返回一个对象,包含配置属性、各个钩子函数调用。
css.walkRules
改为调用Once()
- 最后加上
module.exports.postcss = true
// module.exports = postcss.plugin('postcss-px-to-viewport', function (options) {
module.exports = (options = {}) => {// return function (css) {// css.walkRules(function (rule) {return {postcssPlugin: 'postcss-px-to-viewport',Once(root, { result }){root.walkAtRules(function (rule) {// ...}}}
}module.exports.postcss = true
修改完之后,为了保证其他同事可用。通过patch-package
生成一个补丁包,并提交自己的修改。
npx patch-package postcss-px-to-viewport
再次启动运行,不再有错误提示了。
@rsbuild/plugin-node-polyfill
node polyfill 插件
项目中存在依赖使用 node 模块,使用该模块将 node 核心模块注入到浏览器端。
npm i @rsbuild/plugin-node-polyfill -D
增加配置,作为插件使用:
import { pluginNodePolyfill } from "@rsbuild/plugin-node-polyfill";export default defineConfig({plugins: [//...pluginNodePolyfill(),],
});
可以通过配置globals
来指定需要注入模块的 polyfill
支持.md
文件解析,安装url-loader
安装 webpack 的插件url-loader
npm i url-loader
增加配置,指定加载资源的 loader , 通过tools.rspack
针对 rspack 增加底层配置。
export default defineConfig({// ...tools: {rspack(config, { addRules }) {addRules([{test: /\.md$/,loader: "url-loader",},]);},},
});
@rsbuild/plugin-eslint
运行 eslint 检查
移除掉旧的 eslint 的相关依赖:
npm remove eslint eslint-plugin-vue @vue/eslint-config-standard
对于还用到的依赖,顺便可以重新安装进行升级:
npm i -D @rsbuild/plugin-eslint eslint-plugin-vue vue-eslint-parser eslint-config-prettier globals @eslint/js
eslint@9
推荐配置文件名称为eslint.config.[c|m]js
,我们使用配置文件eslint.config.mjs
。
import PluginVue from "eslint-plugin-vue";
import PluginPrettier from "eslint-config-prettier";
import js from "@eslint/js";
import globals from "globals";
import VueParser from "vue-eslint-parser";export default [js.configs.recommended,...PluginVue.configs["flat/vue2-recommended"],PluginPrettier,{languageOptions: {globals: {...globals.browser},parser: VueParser,parserOptions: {sourceType: "module",ecmaFeatures: {jsx: true,},},},rules: {// ...},},
];
将@rsbuild/plugin-eslint
作为插件配置到rsbuild.config.js
中,可以通过配置项enable
控制是否开启eslint检查。我们启用了eslint@9
的flat config;支持.vue
文件检查,增加后缀。
import { pluginEslint } from "@rsbuild/plugin-eslint";export default defineConfig({plugins: [//...pluginEslint({enable: true,eslintPluginOptions: {cwd: __dirname,configType: "flat",extensions: ["js", "jsx", "vue"],},}),],
});
启动后,发现很多规则检查的代码问题;太多了挨个改不太现实,将这些规则改为警告级别。
由于性能问题,编译时检查代码会导致编译时间过长,建议不开启eslint
.可以单独执行npm run lint
检查代码。
transpileDependencies
配置调整为include
一些存在于node_modules
的第三方模块需要编译时,需要的配置。
{source: {// ...include: [/[\\/]node_modules[\\/]custome-component[\\/]/,],},
}
项目中还使用了svg-sprite-loader
插件的作用可以去查看在 vue 项目中配置使用 svg
配置方式基本没有变,通过tools.bundlerChain
来修改添加 loader
export default defineConfig({bundlerChain(chain, { CHAIN_ID }) {// 修改原svg加载的方式,排除掉需要作为图标使用的svg目录chain.module.rule(CHAIN_ID.RULE.SVG).exclude.add(path.join(__dirname, "./src/assets/svgs"));// 增加一条svg的loader,只处理指定目录chain.module.rule("svg-sprite-loader").test(/\.svg$/).use("svg-sprite-loader").loader("svg-sprite-loader").options({ symbolId: "icon-[name]" }).end().include.add(path.join(__dirname, "./src/assets/svgs")).end();},
});
优化
rsbuild
默认提供了一些优化配置,可以减少开发时编译的模块数量,提高编译速度。
性能分析
https://rsbuild.dev/zh/guide/debug/build-profiling 子这里提供了性能分析的方法,官方提供了rsdoctor
插件,制定了一些规则,loader分析、plugin分析可以很好的查看编译时间消耗在哪里,针对性调整。
npm i @rsdoctor/rspack-plugin -D
rsbuild.config.js
增加配置,在标识需要分析时才执行,因为分析也会加大编译时间
import { RsdoctorRspackPlugin } from "@rsdoctor/rspack-plugin";export default defineConfig({tools: {rspack(config, { addRules, appendPlugins }) {if (process.env.RSDOCTOR === "true") {appendPlugins(new RsdoctorRspackPlugin({mode: "brief",reportCodeType: {noAssetsAndModuleSource: true,},}));}}}
})
这样我们增加脚本配置环境变量,可以选择执行脚本来分析构建
{"scripts": {"dev": "rsbuild dev","build:perf": "NODE_OPTIONS=--max_old_space_size=12288 RSDOCTOR=true rsbuild build","build": "rsbuild build","lint": "eslint --quiet --ext .js,.jsx,.vue ./src "},
}
如果项目太大,内存可能不够会报错,配置增加NODE_OPTIONS=--max_old_space_size=12288
.
配置Lazy Compilation
减少开发时编译的模块数量
可以明显看到启动时间减少。
export default defineConfig({dev: {lazyCompilation: true,// 展示编译进度条progressBar: true,},
})
使用图片压缩
安装插件@rsbuild/plugin-image-compress
启用图片压缩功能。
import { pluginImageCompress } from "@rsbuild/plugin-image-compress";export default defineConfig({plugins: [pluginImageCompress()],
})
拆包
rsbuild
已经默认了拆包规则split-by-experience
按照经验制定的一些拆分策略,比如polyfill \ lodash \ axios
等,还可以自定义将一些依赖拆分。充分利用浏览器的缓存机制,减少请求数量,加快页面加载速度。
export default {performance: {chunkSplit: {strategy: 'split-by-experience',forceSplitting: {echarts: /node_modules[\\/]echarts/,elementUi: /node_modules[\\/]element-ui/,},},},
};
还有一些其他的拆包策略:
split-by-module
按照npm包拆分,每个npm依赖是一个chunk。split-by-size
按照体积大小自动拆分。all-in-one
将所有代码大包到一个chunk中。single-vendor
将所有npm依赖包打到一个chunk中。custom
自定义拆分。
rsbuild
支持动态导入拆包,dynamic import
是es 2020新特性,可以动态加载js模块,rsbuild
会将它们拆分为单独的chunk。
启用Lightning CSS
Lightning CSS
是一个基于 Rust 编写的高性能 CSS 解析、转译和压缩工具.
import { pluginLightningcss } from '@rsbuild/plugin-lightningcss';export default {plugins: [pluginLightningcss()],
};
静态资源内联
将一些静态资源内联到html或js中,减少请求次数。比如图片、视频、css文件等。
开启内联css到html中
export default {output: {inlineStyles: true,},
};
对于内联资源,可以将一些小体积的资源选择性内联,内联资源会增加包的体积,增加了加载时间。而且内联资源不利于缓存,不能重复使用。