先看效果:vercel vite-vue2in3.vercel.app
背景
原项目:vue-cli + vue2 spa项目,总共就 4 个页面,页面之间没什么关系;
目的:想升级 vite + vue3,但又有个页面依赖的组件很难升级 vue3,所以又保留 vue2。
可以微前端,但没必要,其本质是变成两个项目,有点重了。下面是示例项目的创建,也就是主页面为 vue3,子页面是 vue2。
创建 vite 项目
先用 create-vite 创建一个 vue3 项目
根据 vite mpa 文档修改下,添加一个二级页面 sub
此时如果 sub 页面也是 vue3 或者 vanilla 那么已经可以运行了。
为了在项目中同时使用 vue2,我们用 npm alias 来安装 vue2,pnpm add vue2@npm:vue@^2.7
。packages.json 的 dependencies 是这样:"vue2": "npm:vue@^2.7.14"
此时启动页面会报错,原因是 sub 目录下的 .vue 文件没有被正确地编译,因为此时 vite 使用 @vitejs/plugin-vue 来编译项目中的所有 .vue 文件(换句话说 sub/App.vue 被当成 vue3 编译了)。
vite 分别处理 vue2 sfc 和 vue3 sfc
安装这两个插件来处理 vue2 sfc 和 vue3 sfc
pnpm add -D @vitejs/plugin-vue @vitejs/plugin-vue
设置 @vitejs/plugin-vue 不处理 sub 目录下的 .vue 文件,而 sub 目录下的 .vue 文件需要被 @vitejs/plugin-vue2 处理。因此分别使用他们的 include、exclude 配置,如图。同时,因为默认使用 require("vue/compiler-sfc"),而此时项目中 require 会指向 vue3 的这个,需要指定一下 compiler。
// vite.config.ts
......
import vue from '@vitejs/plugin-vue'
import vue2 from '@vitejs/plugin-vue2'
import * as compiler from 'vue2/compiler-sfc'
export default defineConfig({
plugins: [
vue({
exclude: ['src/sub/**'],
}),
vue2({
include: ['src/sub/**/*.vue'],
// @ts-ignore
compiler,
}),
......
这样就大功告吉了!启动!
......
然而并没有什么用
还是报错,但是报错变成了上图,为什么🧐?这行是 import 一个 vue3,难道变成vue2了?
进去断点不了,因为这里不是运行,还要经过 vite 处理的。这毫无疑问是 @vitejs/plugin-vue2 引起的,最终我在 @vitejs/plugin-vue2 代码里面找到这么一个逻辑:(line: 1041-1046)
这插件给 resolve.alias 加了一个 vue 的别名!不知道为什么,很离谱。
上面的报错就是由于 vue 被重定向,而 vue/dist/vue.runtime.esm.js 在本项目中实际指向 vue3,vue/dist 下面根本没有 vue.runtime.esm.js(vue2/dist 下面才有)。
那么加一个 alias,vue -> vue 自己规避一下:
// vite.config.ts
export default defineConfig({
......
resolve: {
alias: [
{ find: 'vue', replacement: 'vue' },
......
启动!真·大功告成!
示例代码仓库: Thy3634/vite-vue2in3 (github.com)
库中 require 的 vue 怎么办?
示例项目没安装任何依赖 vue2 的库,如果有库依赖 vue2,其 require('vue') 会取到 vue3,这毫无疑问会错误。
笔者尝试了 element-ui,直接把 node_modules/element-ui/lib 中搜索 require('vue')/require("vue") 全局替换为 require('vue2') 就好了,这可以用一个 postinstall 脚本处理下。
也可以把组件库提出来稍微改改,放在目录下,安装间接依赖,也能达到目的。