将Vue2+Vue-cli3远古项目重构为Vue2.7+Vite4+TypeScript记录

501 阅读3分钟

由于项目使用ElementUI组件库二次封装&样式重置,加上庞大的shit山代码,升级到Vue3时间方面&技术不可行,所以为了使用新特性的同时保证项目的相对稳定性,决定将Vue版本升级到2.7版本,同时将Vue-cli重构为Vite,加快启动&打包速度。

1、将Vue-cli升级到4版本

"@vue/cli-plugin-babel": "^4.5.18",
"@vue/cli-service": "^4.5.18",

2、将Vue升级到2.7版本

"vue": "^2.7.16",

3、将vue-i18n升级或者去除

由于项目使用vue-i18n实现国际化,因为版本不兼容,所以需要升级或者按需去除

4、将项目中的/deep/或者::v-deep改成:deep()

我这边更改后不生效,所以我没管(狗头)

5、安装Vite及插件

"vite": "^4.5.5",
"vite-plugin-env-compatible": "^2.0.1",
"vite-plugin-html": "^3.2.2",
"@vitejs/plugin-vue2": "^2.3.1",
"@originjs/vite-plugin-commonjs": "^1.0.3",

新建vite.config.js,引入插件

// 环境变量兼容处理,比如vite只识别VITE开头的环境变量,而我们要兼容vuecli中以VUE_APP开头的
// 同时需要在defineConfig中配置envPrefix: ['VUE_APP_']
import envCompatible from 'vite-plugin-env-compatible'
// 因为vite仅支持esm,所以使用这个插件将项目中如require/modules.export等commonjs语法转换为esm
import { viteCommonjs } from '@originjs/vite-plugin-commonjs'
// 使Vue2支持Vite的插件
import vue from '@vitejs/plugin-vue2'
...
plugins: [
    envCompatible(),
    viteCommonjs(),
    vue()
],

6、配置vite.config.js

按照你之前的wepack.config.js或vue.config.js配置迁移过去即可,注意细微差异,如Vite天然支持sass、less、typescript,只需要下载依赖不需要额外配置rules即可使用。

一些webpack特有的配置如chainWebpack、configureWebpack要使用vite配置项重构,由于Vite打包底层是Rollup,所以更多自定义配置可以在 build.rollupOptions 中自行配置。

7、将commonjs转换为esm

刚刚下载的@originjs/vite-plugin-commonjs已经可以帮你将打包产物转成esm了,但是可能node_modules中有些第三方包也是cjs,所以需要在build配置项配置commonjs的选项

build: {
    commonjsOptions: {
        // node_modules中有的包也是commonjs格式,需要开启第三方包cjs模块转换
        transformMixedEsModules: true
    },
}

另外如path.resolve等node模块方法也不允许使用,你可以安装插件实现或者自行实现;

比如我自行实现的path.resolve方法:

export function resolvePathFn(basePath, relativePath) {
    // 将路径分割成段
    const baseSegments = basePath.replace(/^\/|\/$/g, '').split('/')
    const relativeSegments = relativePath.replace(/^\/|\/$/g, '').split('/')
    
    // 合并路径段并去重
    const allSegments = [...baseSegments]
    
    for (const segment of relativeSegments) {
        if (segment === '..') {
            allSegments.pop()
        }
        else if (segment !== '.' && segment !== '' && !allSegments.includes(segment)) {
            allSegments.push(segment)
        }
    }
    
    return '/' + allSegments.join('/')
}

还有如果用到require.context获取模块信息可以改成:

import.meta.glob('./modules/**/*.js', { eager: true })

使用require引入的图片等资源改成import直接引入即可,包括:

// 如路由懒加载的
(resolve) => require(xxx).then(resolve)
// 改成
() => import(xxx)

此时打包就不会出现require is not defined等cjs方面报错了。

8、引入TypeScript

下载依赖

"typescript": "^5.7.2",

新建tsconfig.json配置文件,常用配置如下,具体可以查阅ts官网。

{
    "compilerOptions": {
      "forceConsistentCasingInFileNames": true,
      "target": "esnext",
      "module": "esnext",
      "strict": true,
      "jsx": "preserve",
      "importHelpers": true,
      "moduleResolution": "node",
      "allowJs": true,
      "skipLibCheck": true,
      "esModuleInterop": true,
      "allowSyntheticDefaultImports": true,
      "sourceMap": true,
      "baseUrl": ".",
      "outDir": "./dist",
      "types": [
        "node",
        "vue",
      ],
      "paths": {
        "@/*": ["./src/*"]
      },
      "lib": ["esnext", "dom", "dom.iterable", "scripthost"]
    },
    "include": ["**/*.ts", "**/*.tsx", "**/*.vue"],
    "exclude": ["node_modules", "mock", "build", "dist"],
    "vueCompilerOptions": {
      "extensions": [
        ".vue"
      ],
      "target": 2.7
    }
  }

9、测试重构效果是否生效

新建types/userInfo.d.ts文件测试声明ts类型

type UserInfo = {
    name: string
    age: number
}

新建test.vue文件,写入composition api + ts 代码

<script setup lang="ts">
import { onMounted, reactive, ref } from 'vue';

const count = ref<number>(0)
const increment: Fn<void, void> = () => {
    count.value++
}

const user = reactive<UserInfo>({
    name: 'ouzx',
    age: 25
})
const handleChangeUserName = (): void => {
    user.name = 'ouzx' + Math.random()
}

const handleChangeUserAge = (): void => {
    user.age += 1
}

onMounted(() => {
    alert('调用onMounted钩子')
})
</script>
<template>
    <div>
        <p>计数器:{{ count }}</p>
        <el-button @click="increment">点我加一</el-button>
        <br/><br/>
        <p>名字:{{ user.name }}</p>
        <br/><br/>
        <el-button @click="handleChangeUserName">点我改名</el-button>
        <br/><br/>
        <p>年龄:{{ user.age }}</p>
        <el-button @click="handleChangeUserAge">点我改年龄</el-button>
    </div>
</template>

在package.json配置运行/打包命令并执行

"scripts": {
    "serve": "vite",
    "build": "vite build"
  },

开发/生产环境均可成功运行。

10、总结

1、升级到Vue2.7 / VueCli4版本

2、下载Vite及插件

3、将VueCli配置转为Vite配置

4、将commonjs转换为esm

5、引入TypeScript并编写配置文件