🎯 实在是太慢了,影响开发效率,
📄 既然做了那就彻底点,原来的yarn 也换为了 pnpm , node-sass 换为了 dart-sass, vue-cli 换为 vite 7+
首先要做好思想准备,你的耐心是有限的,但是面对你的是无穷无尽的报错和各种奇怪的原因跑不起来的深渊,不过我觉得报错才是我们的朋友,因为白屏才是最可怕的,什么信息也没有,要自己去找
下面就开始了,我们想一想,如果升级打包工具的话,哪些写法要变呢,聪明的你应该已经想到了,原来依赖vue-cli(webpack)环境的部分肯定是用不了了的,所以我们要找到这些替代方案,比如垫片和适配器和改为新的api方法.还有就是原来解析.vue文件的loader没了,现在要换人了那就是 vite-plugin-vue2 ,还有其他的插件和loader,根据你的项目而定,都需要被替换.
大部分插件都可以在这里面找到 vite插件列表
1 更换环境
首先vite和 vite-plugin-vue2 装上,有两个版本,2.7以上和非2.7以上版本,我这边项目很老,而且还有些原来外包魔改后的库依赖2.5,我就不升级了,老实用vue2.5x的版本
之后就是需要告诉vite如何打包我们的项目,我们只需要写vite.config.js就好了
- 其中 ClosePlugin 是解决因为打包的时候会挂在built in 那个地方不正确退出
- vueDevTools 在vue2中用不了,看了下issue,只能用浏览器插件了,所以只能 codeInspectorPlugin 将就下了
- css预处理器,一般情况下都不需要配置,只需要安装sass等依赖就好了,但是我这边不一样,原来外包的人用了很多的骚操作
- 至于babel和core-js,我直接移除,都这么多年了你还用不支持这些语法的浏览器是不是该升级一下了😊😊😊
- 其他的配置视情况而定,在插件列表中找到对应的插件就好
下面是我的配置,给大家用来参考
js
import { defineConfig, loadEnv } from 'vite'
import { createVuePlugin } from 'vite-plugin-vue2'
import { codeInspectorPlugin } from 'code-inspector-plugin'
import { resolve } from 'path'
import ClosePlugin from './vite-plugin-close.js'
// 使用 defineConfig 定义 Vite 配置
export default defineConfig(({ mode, command }) => {
const env = loadEnv(mode, process.cwd())
console.log(env)
return {
base: './',
server: {
port: 8090,
host: true,
open: true,
proxy: {
'/api-test': {
target: 'http://172.20.203.30:18000',
changeOrigin: true,
rewrite: (p) => {
const s = p.replace('/api-test', '')
// console.log(`proxy to test => ${p}`);
return s
}
}
}
},
// 配置插件
plugins: [
ClosePlugin(),
codeInspectorPlugin({
bundler: 'vite'
}),
// oops vue2 不支持 😭😭😭
// vueDevTools({
// launchEditor: env.VITE_LAUNCH_EDITOR ?? 'code'
// }),
],
css: {
preprocessorOptions: {
scss: {
additionalData: (content, filename) => {
return getAdditionalData(filename) + content
},
// 或者指定禁用特定警告
silenceDeprecations: [
'legacy-js-api', // 旧版 JS API
'global', // 全局变量
'import', // @import 语法
'color', // 旧的颜色函数
'division', // 除法运算
'mixin', // 混合器警告
'selector' // 选择器警告
]
}
}
},
// 配置模块解析规则
resolve: {
// 配置路径别名
alias: {
'@': resolve('src'),
'element-ui-theme': resolve('node_modules/element-ui/packages/theme-chalk/src')
},
// https://cn.vitejs.dev/config/#resolve-extensions
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue']
}
}
})
function getAdditionalData(filePath) {
// 根据文件路径决定是否注入
if (filePath.includes('xr-theme.scss')) {
return '' // 不向 xr-theme.scss 中的文件注入
}
return `
@import "@/styles/xr-theme.scss";
@import "element-ui/packages/theme-chalk/src/common/var.scss";
`
}
css 相关
-
首先如果你换了dart-sass 的话,会有很多的报错 比如
/deep/需要用::v-deep来替换 -
原来scss中还用了这种特殊的语法,在js中使用css变量
less
// the :export directive is the magic sauce for webpack
// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
:export {
colorPrimary: $--color-primary; //0052CC
colorBlack: $--color-black; // 172B4D
colorSuccess: $--color-success; // 36B37E
colorWarning: $--color-warning; // FFAB00
colorDanger: $--color-danger; // FF5630
colorN40: $--color-n40; // DFE1E6
}
在vite中暂时没有找到很好的支持,所以建立了一个js文件用于存放这些变量
arduino
export const XRTheme = {
colorPrimary: '#0052CC',
colorBlack: '#172B4D',
colorSuccess: '#36B37E',
colorWarning: '#FFAB00',
colorDanger: '#FF5630',
colorN40: '#DFE1E6',
// ......
}
之后用regex全局替换,要把全部的import xrTheme from '@/styles/xr-theme.scss' 这种替换为 import { XRTheme as xrTheme } from '@/common/cssTheme.js'.
大概就是这个意思
js
"import xrTheme from '@/styles/xr-theme.scss'".replace(/import +(xrTheme) +from +'@\/styles\/xr-theme.scss'/,"import { XRTheme as $1} from '@/common/cssTheme.js'")
原来人还有一个骚操作,全局注入导入
在vue-cli中的配置如下
arduino
const oneOfsMap = config.module.rule('scss').oneOfs.store
oneOfsMap.forEach((item) => {
item
.use('sass-resources-loader')
.loader('sass-resources-loader')
.options({
resources: [
resolve('src/styles/xr-theme.scss'),
resolve(
'node_modules/element-ui/packages/theme-chalk/src/common/var.scss'
)
]
})
.end()
})
vite中scss有支持,我写为函数形式是因为他们要注入 @import "@/styles/xr-theme.scss"; 但是向 xr-theme.scss 文件注入会无限递归,所以需要限制,但是不知道为什么vue-cli中能自动解决这个问题😒
kotlin
css: {
preprocessorOptions: {
scss: {
additionalData: (content, filename) => {
return getAdditionalData(filename) + content
},
}
}
}
function getAdditionalData(filePath) {
// 根据文件路径决定是否注入
if (filePath.includes('xr-theme.scss')) {
return '' // 不向 xr-theme.scss 中的文件注入
}
return `
@import "@/styles/xr-theme.scss";
@import "element-ui/packages/theme-chalk/src/common/var.scss";
`
}
当然还会有些警告,比如scss某些语法已经过时了,但是感觉暂时也没有很好的方法来换他们,因为感觉不是一行regex能搞定的,最好有语法分析,至于官方的迁移工具是不支持.vue文件的.所以暂时没时间搞了
这一通操作下来改了180+个文件
js和环境相关
原来导入了一些函数,但是不存在,cli中不会报错,但是vite中会找不到,需要对这些前人留下的坑全部给填掉
process.env.VUE_APP_CONTACT_URL 这种变量需要被替换为 import.meta.env.VITE_CONTACT_URL 记得VUE开头要换位VITE开头,不然找不到
process.env.VUE_APP_([^ ]+) 替换为 import.meta.env.VITE_($1)
require api替换 require('@/assets/callCenter/ring.png') 替换为 new URL('@/assets/img/head.png', import.meta.url).href
regex 如下 "require('@/assets/callCenter/ring.png')".replace(/require\('([^']+)'\)/,"new URL('$1', import.meta.url).href")
其中store 和 router 使用了动态导入,但是require.context是webpack的语法,看vite官方文档,有一个api比较像,注意eager要为true,不然返回的是一个Map<string,Promise<Module>>的类型
importAll(require.context('./modules', false, /.js$/)) 替换为 importAll(import.meta.glob('./modules/*.js', { eager: true }))
importAll中是modules.keys().forEach()写法,需要替换为Object.keys(modules),而且key可能和原来预想的不同,我就是因为这个原因直接白屏了,因为后面模块名不对导致加载逻辑全错,下面贴出代码对比
javascript
const syncRouters = []
const asyncRouterMap = []
-function importAll(r) {
+function importAll(modules) {
let manageIndex = -1
- r.keys().forEach(key => {
- const fileName = key.slice(2, -3)
- const itemRouter = r(key).default
+
+ Object.keys(modules).forEach(moduleKey => {
+ const fileName = moduleKey.slice(2, -3)
+ const itemRouter = modules[moduleKey].default
+ console.log(fileName,moduleKey,itemRouter)
+
if (fileName.includes('sync')) {
syncRouters.push(...itemRouter)
} else {
asyncRouterMap.unshift(asyncRouterMap.splice(oaIndex, 1)[0])
}
}
}
-importAll(require.context('./modules', false, /\.js$/))
+importAll(import.meta.glob('./modules/*.js', { eager: true }))
export {
syncRouters,
原来还在vue代码中用过path.resolve 这种nodejs中的api,直接写一个垫片resolve之后导入,全局替换导入模块,当然你也可以写vite插件替换或者模拟虚拟模块解析之后映射,至于实现我是找ai写的就不贴了
javascript
import { mapGetters } from 'vuex'
import { getNavMenus } from './components/utils'
-import path from 'path'
+import { path } from '@/common/path'
export default {
name: 'CrmLayout',
auth = this.$auth(item.permissions.join('.'))
}
if (!item.hidden && auth) {
sideMenus.push({
...item,
path: path.resolve(mainPath, item.path)
这一波又是60+个文件的修改
第三方依赖兼容
外包使用的vue2-org-tree 的库中找不到某个文件,其实是路径解析的问题,我们需要明确后缀,注意:vite会查看pkg.json,优先使用module
这个操作需要改源码,直接用pnpm patch功能就好,非常方便
javascript
// pkg.json
{
"main": "dist/index.js",
"module": "src/main.js",
}
// index.js
- import Vue2OrgTree from './components/org-tree'
+ import Vue2OrgTree from './components/org-tree.vue'
项目中外包使用了自己魔改的el-ui库,并且基于魔改库重写了插件,比如 el-bigdata-table 中的 render.js 用到了jsx语法,感觉没必要为了一个依赖引入jsx,所以建了一个子工程,写一套打包配置来打包为h函数版本,最后拷贝到项目,聪明的你可能要问为什么要自己写打包配置,因为他的包中只有源码,没有上传打包配置😂,下面是jsx版本
kotlin
export default function render(h, oldRender) {
return (
<div
style={[{height: `${this.table.virtualBodyHeight}px`}]}
class={['el-table__virtual-wrapper', {'el-table--fixed__virtual-wrapper': this.fixed}]}
v-mousewheel={this.table.handleFixedMousewheel}
>
<div style={[{transform: `translateY(${this.table.innerTop}px)`}]}>
{
oldRender.call(this, h)
}
</div>
</div>
);
}
打包部分
打包还有点小插曲,就是卡built in xxx.xxxs这里,看国外stack overflow中和一些文章中说要写个插件,其实就是在结束的钩子中调用 process.exit(0) 系统调用
javascript
export default function ClosePlugin() {
return {
name: 'ClosePlugin', // required, will show up in warnings and errors
// use this to catch errors when building
buildEnd(error) {
if (error) {
console.error('Error bundling')
console.error(error)
process.exit(1)
} else {
console.log('Build ended')
}
},
// use this to catch the end of a build without errors
closeBundle(id) {
console.log('Bundle closed')
process.exit(0)
}
}
}
模板文件修改,主要是加入 <script type="module" src="/src/main.js"></script>,这样vite才知道入口
xml
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="icon" href="favicon.ico">
<title>CRM</title>
</head>
<body>
<noscript>
</noscript>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
记得发布的vite的base参数也要调整,nginx也是
可能还有遗漏,但是大概就是这些了,感谢观看😊😊😊!