
我为什么要迁移到Rsbuild?
我们部门开发的产品构建工具都是@vue/cli-service4.5以下的版本,且使用babel处理低版本浏览器兼容性问题,项目又很庞大,每次构建都需要5分钟左右。这月项目不是很忙,决定对本部门产品构建工具进行升级!
在决定升级后对比了Vite和Rsbuild:
- 部门的项目兼容IE10、11 切换到Vite后很难处理
- Rsbuild 兼容了大部分webpack配置和插件,使用SWC兼容低版本浏览器,编译速度快。

迁移第一步:删除所有vuecli和babel相关插件
NodeV12
切换为NodeV18
(Node.js >= 16
都可以)
js
nvm use 18
删除node_modules
和package.lock.json
以及babel.config.js
js
rimraf node_modules package.lock.json babel.config.js
删除package.json
中所有devDependencies
依赖(也可以先只删除vuecli和babel相关依赖,根据自己项目而定)。 我的项目构建比较早,随着版本迭代引入了很多不必要的插件,全部删除后瞬间清爽了很多。
js
"devDependencies": {
"@babel/polyfill": "^7.12.1",
"@babel/core": "^7.18.2",
"@vue/cli-plugin-babel": "~4.5.17",
"@vue/cli-plugin-eslint": "~4.5.17",
"@vue/cli-plugin-router": "~4.5.17",
"@vue/cli-plugin-vuex": "~4.5.17",
"@vue/cli-service": "~4.5.17",
"babel-eslint": "^10.1.0",
"babel-plugin-import": "^1.13.5",
"copy-webpack-plugin": "^5.1.1",
"eslint": "^7.32.0",
"eslint-config-prettier": "^7.0.0",
...
"less": "^3.13.1",
"less-loader": "^6.2.0",
"prettier": "^2.6.2",
....
}
迁移第二步:使用Rsbuild构建新项目
因为考虑到项目文件有点多(单路由文件超180),直接修改package.json
再重新拉依赖会有很多报错,所以还是决定新开一个项目,再把依赖、源代码慢慢挪过来。
js
npx create-rsbuild --dir rsbuild-jsas --template vue2-js
这样构建出一个比较干净的项目后,把自己项目的package.json
中的依赖复制过来,执行npm install
。然后启动项目查看是否报错。
迁移第三步:源码迁移
成功构建好项目后,先迁移公共组件代码,vuex
、router
、axios
等,运行没有报错后再进行业务代码迁移。这一步大部分项目运行时会报错。
/deep/修改为::v-deep
vue升级后不再支持/deep/
样式穿透,统一改为::v-deep
。
修改导入扩展名
当我们导入.vue
文件时,不需要具体到文件的后缀系统就能识别到文件,这是因为vuecli
构建的项目自动帮我们添加了.vue
后缀。但是Rsbuild
构建的项目需要我们手动添加,在rsbuild.config.mjs
文件中添加代码。
注意:resolve.extensions今添加常用的文件即可,配置的过多会导致性能问题
js
...
resolve: {
...
extensions: ['.js', '.jsx', '.vue']
},
...
环境变量
Vue CLI 默认会将
VUE_APP_
开头的环境变量注入到 client 代码中,而 Rsbuild 默认会注入PUBLIC_
开头的环境变量。为了兼容 Vue CLI 的行为,你可以手动调用 Rsbuild 提供的 loadEnv 方法来读取VUE_APP_
开头的环境变量,并通过 source.define 配置项注入到 client 代码中。
js
import { defineConfig, loadEnv } from '@rsbuild/core';
const { publicVars } = loadEnv({ prefixes: ['VUE_APP_'] });
export default defineConfig({
source: {
define: publicVars,
},
});
处理源码中的NodeJs模块
以前在源码中使用NodeJs模块,打包后在浏览器环境下依然正常运行,是因为webpack帮我们做了处理,自动替换为浏览器相关API,但是Rsbuild需要额外引入依赖Node Polyfill 插件,并添加如下配置
js
import { pluginNodePolyfill } from "@rsbuild/plugin-node-polyfill";
export default defineConfig({
plugins: [pluginNodePolyfill()],
});
我项目使用NodeJs模块的地方不多,所以我直接把相关代码直接用浏览器兼容的API重写了,就没安装这个包。
指定HTML模版
Vue CLI 默认使用
public/index.html
文件作为 HTML 模板。在 Rsbuild 中,你可以通过 html.template 来指定 HTML 模板:
js
export default defineConfig({
html: {
template: './public/index.html',
},
});
按需引入组件
之前项目使用了ant-design-vue
的一个组件,在Rsbuild中怎么可以按需引入这个组件呢?可以参考这个配置:
js
export default defineConfig({
source: {
transformImport: [
{ libraryName: "ant-design-vue", libraryDirectory: "es", style: true },
],
},
});
全局注册less变量
js
plugins: [
...
pluginLess({
lessLoaderOptions: {
lessOptions: {
// additionalData: `@import "@/styles/less/variables.less`
globalVars: {
'primary-color': '#0096f7',
'boder-color': '#dfdfdf',
'warning-color': '#ffcc00'
}
}
}
}),
],
自定义拆包策略
Rsbuild内置了一些拆包策略,想要一些自定义配置可以使用chunkSplit.override
配置。不推荐使用## chunkSplit.strategy: custom
,除非你非常清楚你要每个包应该怎么拆分。
chunkSplit.override
会根据split-by-experience
模式下自定义策略。 这里我把echarts
单独拆分。
js
performance: {
removeConsole: true,
chunkSplit: {
override: {
cacheGroups: {
echarts: {
test: /node_modules[\\/]echarts[\\/]/,
name: 'lib-echarts',
chunks: 'all'
},
}
}
}
// 与构建性能、运行时性能有关的选项
},
兼容IE浏览器需要做哪些处理
项目要求兼容IE11,node_modules
下很多包交付的代码并不支持IE,所以我们需要额外编辑node_modules
下的依赖(默认是不编译的)。
这样会编译node_modules
下除了core-js
以外的所有依赖。
js
source: {
...
include: [{ not: /[\\/]core-js[\\/]/ }], //swc编译所有的js文件 排除node_moduled下的core.js 二次编译会导致运行时问题
},
最后别忘了修改.browserslistrc
。
js
[production]
> 0.5%
not dead
IE 11
[development]
chrome >= 87
edge >= 88
firefox >= 78
safari >= 14
js文件中导出jsx代码编译报错怎么解决

我在js文件中写了jsx代码,编译时报错,此时不得不安装babel插件了。
js
import { pluginBabel } from '@rsbuild/plugin-babel';
plugins: [
pluginBabel({
include: /\.TooltipTool\.js$/,
// 建议 exclude node_modules 目录以提升构建性能
exclude: /[\\/]node_modules[\\/]/,
}),
...
],
总结
以上就是项目迁移的整个过程,附上迁移前后性能提升对比。
项目启动时间由4分29秒缩减为35.8秒,提升7.5倍。


打包时间由8分40秒缩减至1分1.78秒,速度提升了约8.42倍。


首页优化: 进入系统首页原来共发起了552项请求,共36.8MB加载时间26.16秒。Rsbuild打包后,则发起了106项请求,共13.4MB,加载时间为4.24秒。

最后,附上完整配置以供大家参考。
js
const path = require('path');
import { defineConfig, loadEnv } from '@rsbuild/core';
import { pluginVue2 } from '@rsbuild/plugin-vue2';
import { pluginLess } from '@rsbuild/plugin-less';
import { pluginVue2Jsx } from '@rsbuild/plugin-vue2-jsx';
import { pluginBabel } from '@rsbuild/plugin-babel';
import { pluginBasicSsl } from '@rsbuild/plugin-basic-ssl';
import { RsdoctorRspackPlugin } from '@rsdoctor/rspack-plugin';
const { publicVars: vueEnvs } = loadEnv({ prefixes: ['VUE_APP_'] });
const resolve = dir => {
return path.join(__dirname, dir);
};
export default defineConfig({
plugins: [
pluginBabel({
include: /\.TooltipTool\.js$/,
// 建议 exclude node_modules 目录以提升构建性能
exclude: /[\\/]node_modules[\\/]/,
}),
pluginVue2({
splitChunks: {
vue: true,
router: true
}
}),
pluginVue2Jsx(),
pluginLess({
lessLoaderOptions: {
lessOptions: {
// additionalData: `@import "@/styles/less/variables.less`
globalVars: {
'primary-color': '#0096f7',
'boder-color': '#dfdfdf',
'warning-color': '#ffcc00'
}
}
}
}),
pluginBasicSsl()
],
server: {
port: 9529,
// 跨域代理
proxy: {
'/api': {
target: 'https://10.0.100.235',
secure: false,
// target: 'https://10.0.100.101',
changeOrigin: true,
//重点在下面,下面这个把本地调试403问题解决掉了
onProxyReq: proxyReq => {
proxyReq.removeHeader('referer'); //移除请求头
proxyReq.removeHeader('origin'); //移除请求头
}
}
},
// 移除请求报错全屏显示错误
client: {
overlay: false
}
},
resolve: {
alias: {
'@': './src'
},
extensions: ['.js', '.jsx', '.vue','.less', '.css']
},
// 与源代码解析、编译方式相关的选项
source: {
entry: {
index: './src/main.js'
},
define: {
...vueEnvs
},
include: [{ not: /[\\/]core-js[\\/]/ }], //swc编译所有的js文件 排除node_moduled下的core.js 二次编译会导致运行时问题
},
// 与构建产物有关的选项
output: {
legalComments: 'none',
polyfill: 'entry',
copy: [
{
from: resolve('./static'),
to: resolve('./dist/static')
}
],
distPath: {
root: 'dist'
},
sourceMap: {
js: process.env.NODE_ENV == 'development' ? 'cheap-module-source-map' : false,
css: true ,
},
},
tools: {
bundlerChain: (chain, { CHAIN_ID }) => {
if(process.env.RSDOCTOR){
chain.plugin('Rsdoctor').use(RsdoctorRspackPlugin, [
{
// 插件选项
disableClientServer: false,
features: ['lite', 'loader', 'plugins', 'bundle'],
}
]);
}
}
},
// 与 HTML 生成有关的选项
html: {
template: './public/index.html'
},
performance: {
removeConsole: true,
chunkSplit: {
override: {
cacheGroups: {
echarts: {
test: /node_modules[\\/]echarts[\\/]/,
name: 'lib-echarts',
chunks: 'all'
},
}
}
}
}
});