跳转至

10分钟看完 Vue3 工程适配

1. 开始

本文为 Vue3.x 工程适配记录。项目中很多老工具、组件都是基于 Vue2.x 的,而且还在用,所以基本是戴着镣铐跳舞,需要向下兼容,同时追求高效、简单。

适配大体分两类,运行时和编译时,运行时适配包括各种语法,编译时适配包括 Webpack LoaderPlugintranspileDependencies 等方面。

2. 基础配置

首先基于当前项目架构,搭建了 vite 基础配置

当前架构是什么?就是形如 src/project/usermonorepo 模式。

还有对 devcloudnode 版本升级,打包产物位置的更新,等这些比较小的点。

3. 适配

3.1. TIP_PLATFORM_NAME 适配

一个 Vite 插件 即可解决。

但是对于第三方库,需要额外的技巧。

需要在 vite.config.ts 中的 optimizeDepsExcludes 中将其排除,否则在 vite 的依赖分析的时候就会提前报错,此时还没到插件的处理时机。

这样处理后,一些用 commonjs 的三方库可能会报错,这时又需要添加 optimizeDepsIncludes,让插件处理这些依赖。

3.2. 组件库适配

一些三方库是以源码方式提供的,比如 press-plusVue2.x 时可以配置 transpileDependencies,但是 Vite 没有对应的配置项。

可以这么处理:

  1. 将三方库复制到 src 下的某个目录下
  2. 为三方库配置 alias,指向 src 某目录下
  3. 配置 tsconfig.jsoncompileOptions.paths

除组件库外,一些类似 pmd-tools 的三方库也可以这样处理。需要注意的是,一旦配置 alias 为本地源码后,一些依赖要额外安装(比如 vue-runtime-helpers),否则在 pnpm 项目中可能找不到。

3.3. 条件编译

项目支持条件编译,一些地方使用它会非常简单。比如 src/component/logic/tip-merchant/jump-handle/helper.js 中 对 configInfo 的适配。

3.4. node.js 版本

node 版本必须 >= 16,可以使用 nvm 切换,非常方便。

devcolud 已支持。

3.5. template v-for

Vue2.xVue3.x:key 使用互不兼容,前者不能放到 template 上,后者必须放到 template 上,且是 vscode 插件的报错,不是 eslint 的报错。

一个简单的解决办法就是把 template 改成 div/span

参考:https://v3-migration.vuejs.org/zh/breaking-changes/key-attribute.html

3.6. vite 依赖缓存

vite 有依赖缓存,禁用缓存可以用 sudo npm run dev --force

3.7. vite root

修改了 vite.config.ts 中的 root 后,比如设置为 subProject 路径,需要更改 envDir,否则客户端拿不到环境变量。

3.8. 支持 history 模式

router 需要改成:

1
2
3
4
5
6
7
const baseUrl = import.meta.env.VITE_ROUTER_BASE;

const router = createRouter({
  // hash 模式为 createWebHashHistory(),
  history: createWebHistory(baseUrl),
  routes,
});

vite.config.ts 中增加额外配置,支持上云和 history 模式。

const vueAppBase = env.VUE_APP_PUBLICPATH;
const experimentalConfig = vueAppBase ? {
  experimental: {
    renderBuiltUrl(filename: string, { hostId, hostType, type }: {
      hostId: string;
      hostType: string;
      type: string;
    }) {
      console.log('[experimental] ', hostType, hostId, type, filename);

      return `${removeLastSlash(vueAppBase)}/${filename}`;
    },
  },
} : {};

return {
  root: subProjectRoot,
  envDir: process.cwd(),

  base: vueAppBase || './',
  ...experimentalConfig,
}

3.9. 其他

还有很多其他小的适配和改造,直接看官方文档就行了。

比如 .sync 的去除,这里我写了脚本对 component 子仓库进行了批量替换,

4. 实例

Vue 中的实例分应用实例和组件实例,Vue2.x 中开发者不用太关心,因为它用了原型链,在组件实例尝试获取应用实例的属性,也可以拿到。Vue3.x 中则不一样。

比如 $ebus 的适配,首先 $ebus 一定是挂载在应用实例上,这样每个组件才能拿到相同的。下面代码是错误的:

1
2
3
4
5
6
7
app = createApp(component, {
  ...propsData,
}).mount(el);

const eBus = new EventBus();
// 挂载目标不对
app.config.globalProperties.$ebus = eBus;

并且挂载时机也必须在 mount 之前。下面的代码就是错误的:

1
2
3
4
5
6
7
8
app = createApp(component, {
  ...propsData,
})
componentInstance = app.mount(el);

const eBus = new EventBus();
// 挂载太晚了
app.config.globalProperties.$ebus = eBus;

贴两张图,直观感受下应用实例和组件示例的差别。应用实例:

组件实例:

5. 总结

好的工程配置可以事半功倍,升级 Vue3 在我看来不只是语法的升级,更重要的是工程的配置。你可以在 Vue3 中写 hooks,也可以写 options API,这些都是小事,重要的是你得有这样一套配置,并且对基础组件、基础工具库做好兼容。

复杂的事情简单做,而不是反过来。一天天吭哧吭哧加班,装作很忙的样子,最后代码写的像屎一样。对应到 Vue3 适配,Press 系列组件库、底层工具都是兼容 Vue3 的,所以上层改动很少,如果由 KPI 驱动型的人来做,估计又要重新写一套组件库,当作自己辉煌的 KPI 了。

至于 Press 系列组件库是如何兼容 Vue3 的,可以参考之前的文章