背景
前端项目工程的开发和编译时间很慢,通过调研vite,发现开发环境的使用还是挺快的,所以在项目中进行开发模式的实践,最后也落地了一下vite开发的模式。
当前,公司项目编译还是采用webapck编译部署,项目部署普遍在3min以上,有的可能甚至更长,影响编译部署测试环境的体验。
所以想利用vite在部署编译上能不能有落地,但经过一段时间的研究发现,并不能vite生产编译不是很好,一些公司内部的webpack都需要进行重写,成本很高,构建速度甚至还不如webpack。随着rspack的推出,官方据说项目接入之后会有5-10倍的构建提升 ,编译时间越长,优化空间也是巨大的 。所以对rspack研究了相当多的时间(几个月),现在已经在c2c这个大项目中进行了部署上线,取得了不错的效果。从之前的3-4min到现在的39s左右,算上部署过程中的安装包和部署校验啥的,总共花费时间也在1min之内。
填坑经历
-
umd/amd 适配不是很好,导致部署测试时,报
require is not defined
相关issue 在 github.com/web-infra-d... github.com/web-infra-d... 所以我们暂时可以通过升级相关报错的依赖包,或者fork相关依赖包,以支持一下最新amd/umd规范。 -
每次构建产物的不可预料性,这个问题的出现,引起了有放弃rspack的想法,后来官方还是很快的速度解决了,为rspack点赞👍 相关issue github.com/web-infra-d... github.com/web-infra-d...
-
构建样式排序问题,这个问题已经解决 github.com/web-infra-d...
-
rspack 支持提取css github.com/web-infra-d... 这个问题困扰了我很长时间,rspack官方也没有人力去解决,然后我就开始根据rspack的github.com/web-infra-d... 贡献思路,为github.com/vuejs/vue-l... vue-loader v15.x贡献了# support experimental inline match resource,以支持rspack提取css功能。
-
当然下面回归测试页面也不是一帆风顺的,github.com/web-infra-d... 不过这些小问题很快得到了解决。
接入
node版本使用14.20以上
非webpack5项目
升级项目到webpack5,已经是webpack5可以下一步
js
使用"vue-loader" 指定版本 15.11.1
使用 @vue/cli-service@5.0.0-beta.6
@vue/cli-plugin-typescript@5.0.0-beta.6
@vue/cli-plugin-babel@5.0.0-beta.6
"@vue/babel-preset-app": "^5.0.8"
"@rspack/cli": "^0.3.10"
"webpack": "5.89.0"
webpack5项目
perl
使用"vue-loader" 指定版本 15.11.1
"@rspack/cli": "^0.3.10"
"webpack": "5.89.0"
根目录添加 rspack.confg.js
javascript
const path = require('node:path')
const rspack = require('@rspack/core')
const { VueLoaderPlugin } = require('vue-loader')
const isProduction = process.env.NODE_ENV === 'production'
const imgPublicPath = 'https://img1.demo.com/path/'
const assetsPublicPath = 'https://s1.demo.com/path/'
const publicPath = isProduction ? assetsPublicPath : '/path/'
function resolve(dir) {
return path.join(process.cwd(), dir)
}
const rspackPlugins = [
new VueLoaderPlugin(),
new rspack.HtmlRspackPlugin({
template: 'public/index.html',
filename: isProduction ? 'webserver/index.html' : 'index.html'
}),
new rspack.CopyRspackPlugin({
patterns: [
{
from: 'public',
globOptions: {
ignore: ['**/index.html']
}
}
]
})
]
/**
* @type {import('@rspack/cli').Configuration}
*/
const baseWebpackConfig = {
cache: false,
mode: isProduction ? 'production' : 'development',
context: __dirname,
entry: {
main: './src/main.ts'
},
output: {
clean: true,
publicPath,
cssFilename: 'static/css/[name].[contenthash:8].css',
filename: 'static/js/[name].[contenthash:8].js',
cssChunkFilename: 'static/css/[name].[contenthash:8].css',
chunkFilename: 'static/js/[name].[contenthash:8].js'
},
target: ['web', 'es5'],
builtins: {
PresetEnv: {
mode: 'entry',
targets: ['2015']
},
pluginImport: [
{
libraryName: 'vant',
style: '{{ kebabCase member }}/style/index.js'
}
]
},
devServer: {
https: true,
hot: true,
host: 'local-ip',
port: 8088,
allowedHosts: 'all',
historyApiFallback: true
},
module: {
rules: [
{
test: /.vue$/,
use: [
{
loader: 'vue-loader',
options: {
experimentalInlineMatchResource: true
}
}
]
},
{
test: /.tsx$/,
use: [
{
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-typescript', { allExtensions: true, isTSX: true }],
[
// 注意 @vue/babel-preset-jsx适配vue2.x @vue/babel-plugin-jsx 适配vue3.x
'@vue/babel-preset-jsx',
{
injectH: false
}
]
]
}
}
]
},
{
test: /.css$/,
use: ['postcss-loader'],
type: 'css'
},
{
test: /.scss$/,
use: [
'postcss-loader',
'sass-loader',
{
loader: 'style-resources-loader',
options: {
patterns: [
resolve('src/assets/vars.scss'),
resolve('src/assets/common.scss')
],
injector: 'prepend'
}
}
],
type: 'css'
},
{
test: /.less$/,
use: ['postcss-loader', 'less-loader'],
type: 'css'
},
{
resourceQuery: /lang=ts/, // 如果需要在 Vue SFC 里使用 Typescript, 请添加该规则
type: 'ts'
},
{
test: /.(png|jpe?g|gif|svg)/i,
type: 'asset',
generator: {
publicPath: isProduction ? imgPublicPath : publicPath,
filename: 'static/img/[name].[hash:8][ext]'
}
},
{
test: /.(png|jpe?g|gif|svg)/i,
type: 'asset/inline',
generator: {
publicPath: isProduction ? imgPublicPath : publicPath,
filename: 'static/img/[name].[hash:8][ext]'
}
},
{
test: /.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset/resource',
generator: {
filename: 'static/font/[name].[hash:8][ext]'
}
}
]
},
experiments: {
css: true
},
resolve: {
extensions: ['.mjs', '.js', 'jsx', '.ts', '.tsx', '.vue'],
alias: {
'@': resolve('src'),
'@src': resolve('src'),
'vue-loader': resolve('node_modules/vue-loader')
}
},
optimization: {
realContentHash: true,
splitChunks: {
cacheGroups: {
someVendor: {
chunks: 'all',
minChunks: 2
}
}
}
},
plugins: rspackPlugins
// stats: 'verbose'
}
module.exports = baseWebpackConfig
注意事项
-
因为rspack对AMD规范没有全面支持。所以遇到打包后页面报
require is not defined,需要找到相关依赖包,升级到最新的AMD规范的包。相关issue在
所以需要升级相关依赖包看能不能解决。
-
如果项目中采用下图中的方式书写代码,里面包含render jsx,需要在script中添加lang="tsx",让tsx loader进行处理
-
注意 @vue/babel-preset-jsx适配vue2.x @vue/babel-plugin-jsx 适配vue3.x
-
script配置 build采用rspack build进行编译,开发的话可以采用,dev:rspack或者dev:vuecli,由于rspack对dev支持不是特别好,可以开发采用cli的。当然开发也可以采用vite
js
"build:rspack": "rspack build",
"build:vuecli": "vue-cli-service build --no-module",
"dev:rspack": "rspack serve",
"dev:vuecli": "vue-cli-service serve",
"dev": "npm run dev:vuecli",
"build": "npm run build:rspack"
回归测试
- 项目部署到测试环境后,对项目中的路由都进行一下回归,看看页面有问题没,console有没有异常。可以小组内分模块进行回归。
- 测试一下离线包和预渲染等
- 上线后观察sentry有没有异常
接入效果
接入前
接入之后

总结
虽然接入rspack不是一帆风顺的,但结果是好的,并且自己也收获了很多,学习了相关的webpack,vue-loader知识等。rspack对webpack相关的生态支持还是比较好的,比如公司内部的离线包和sentry插件都不用改,直接可以进行使用。接入成本是很低的,最后也可以考虑集成到内部脚手架,方便其他同学使用。