只用一套代码运行Vue2 和 Vue3的组件库?原来是这么做的

您所在的位置:网站首页 js编译时和运行时的区别 只用一套代码运行Vue2 和 Vue3的组件库?原来是这么做的

只用一套代码运行Vue2 和 Vue3的组件库?原来是这么做的

2023-05-30 07:05| 来源: 网络整理| 查看: 265

前言

大家好,今天分享的是关于如何实现Vue 2.7 与 Vue 3.x 同时兼容的组件库。为什么需要这种组件库呢?原因很简单:

公司内部很多项目需要接入新的UI标准,并且项目的Vue版本较低,且大部分都是 Vue 2.6的版本,而Vue 2.7可以做到向下兼容,同时向上可以兼容绝大多数Vue 3.x的特性。 未来很多新项目肯定都会使用 Vue 3.x来开发,如果再实现一套 Vue 2.6版本的组件库,又无法满足未来需求。

基于此,如果能实现上述能力,可以节省大量的重复代码开发,而且方便了项目迁移。

同时在写这篇文章时,组件库已经成功在公司内部上线,并且接入了多个项目,目前整体来看方案还是非常可行的,但是还不够通用,后续也考虑开发一套脚手架。

1. 业内方案

其实在开发这套组件库之前,在网上也搜索了很多组件库,并没有可以借鉴的脚手架或者组件库。整体方案也是经过不断迭代才完善的。

直到最近才看见,其实华为云已经开源一款组件库TinyVue组件库 , 看了下其实实现也大同小异:

Vue不同版本之间运行时的适配层,例如Teleport、Fragment在Vue3有,但是在Vue2并没有,所以需要进行适配,可以看到下方的是 TinyVue 的实现,很多Vue Api通过适配层统一导入,就可以做到多个版本适配。

项目存在多个Vue版本,可以通过npm别名来安装:

...等等

那么开发出这么一套组件库,需要做哪些处理呢,下面我们来看看具体差异

2. Vue 2.7 与 Vue 3.x的差异

虽然 Vue 2.7 的发布日志看起来与Vue3.x的区别不大,但是实际差异只有在开发时才会体现出来。下面从几个角度来看看,到底差别在哪。

2.1 类型导出

以下类型,在 Vue 2.7是没有导出的:

import { App, StyleValue, Slot, Slots, VNodeTypes, RenderFunction, ... } from 'vue'

既然在Vue2中没有,那么我们其实可以自己实现类型声明,并导出vue模块:

declare module 'vue' { import * as CSS from 'csstype'; export * from 'vue2'; export interface CSSProperties extends CSS.Properties, CSS.PropertiesHyphen { [v: `--${string}`]: string | number | undefined; } export type ComponentInternalInstance = any; export type VNodeNormalizedChildren = any; export type App = any; export type createApp = any; export type StyleValue = string | CSSProperties | Array; // ...其他 }

可以看到,重新声明了vue模块,并且在内部导入了Vue2的类型。实现了那些没有的类型定义。

2.2 API差异

同样的,Vue 2.7 相比 Vue3还缺少一些API, 如:

import { cloneVNode, createVNode, Fragment, render, Teleport, Transition, TransitionGroup, ... } from 'vue'

以上API都是在Vue3中存在,但是Vue 2.7缺少的。

APIVue 3.xVue 2.7cloneVNode✅❌createVNode✅❌Fragment✅❌render✅❌Teleport✅❌Transition✅❌TransitionGroup✅❌...✅❌

当然 还不止这些API..

2.3 模板编译

我们先来看下 Vue 2.7和Vue3的模板编译有什么不一样

Vue 3.xVue 2.7

可以看到仅仅是VNode的创建,实现方式都是不一样的。那么还有哪些呢?

模板编译Vue 3.xVue 2.7v-model:xxx 写法✅❌创建VNode方式createVNodevm._cv-slots写法(jsx)v-slotsscopedSlots......... 3. 为什么不兼容 Vue 2.6

如果想要用Vue 2.6 去实现 Vue 3.x的代码,那么我觉的大可不必,收益并不大。

我们知道Vue 2.7是完全向下兼容的,一个Vue 2.6 项目想要升级 Vue 2.7,只需要升级一下编译工具版本 和 Vue版本即可。

但是,如果升级Vue 3.x的代价就太大了,先不说一些 break change, 一旦你的项目依赖了一些外部npm组件,它本身是已经编译好的代码,并且是Vue 2.6的产物,项目是绝对运行不起来的。而Vue 2.7则完全不用考虑这些。

由此可以得出,升级Vue 2.7的成本其实并不高。何必还要单独考虑去实现Vue 2.6的适配呢?

4. 编译兼容方案 4.1 Vue 版本兼容

如果你想切换不同的Vue版本,首先项目得安装两个版本的Vue

npm i vue2@npm:vue@^2.7.14 vue3@npm:vue@^3.2.45

然后在Vite配置中加入,alias解析,根据当前构建的Vue版本,去选择对应的产物路径:

resolve: { alias: { vue: isVue3() ? path.resolve( path.dirname(require.resolve('vue3')), 'dist/vue.runtime.esm-bundler.js' ) : path.resolve(path.dirname(require.resolve('vue2')), 'vue.runtime.esm.js'); } }

编译后:

// Vue 2.7 import { reactive } from '/xxx/node_modules/vue2/vue.runtime.esm.js' // Vue 3.x import { reactive } from '/xxx/node_modules/vue3/dist/vue.runtime.esm-bundler.js' 4.2 模板编译兼容

以Vite为例,如果想要编译vue, 那么正常需要@vitejs/plugin-vue插件的,但是它支持Vue3的模板编译,所以还要安装两个sfc编译插件:

npm i @vitejs/[email protected] npm i @vitejs/[email protected]

然后配置一下:

{ plugins: [ isVue3() ? vitePluginVue({ include: [/\.vue$/, /\.md$/], }) : vitePluginVue2({ include: [/\.vue$/, /\.md$/], }), ] }

但是这样肯定是不行的,为什么呢?

翻一下源码就能看到,它依赖了vue/compiler-sfc, 两个插件都是依赖了相同的sfc编译器。那结果显而易见,产物最后肯定还是一样的。

所以它也需要安装不同的版本:

npm i @vue/compiler-sfc-vue2@npm:@vue/compiler-sfc@^2.7.14 npm i @vue/compiler-sfc-vue3@npm:@vue/compiler-sfc@^3.2.45

然后通过设置compiler,可以指定使用哪个编译器编译。

{ plugins: [ isVue3() ? vitePluginVue({ include: [/\.vue$/, /\.md$/], compiler: vue3Compiler as any }) : vitePluginVue2({ include: [/\.vue$/, /\.md$/], compiler: vue2Compiler as any }), ] } 4.3 jsx编译兼容

在组件库开发中,难免会使用到jsx的语法,我们同样要做切换。就不多废话了,直接安装:

npm i @vitejs/plugin-vue-jsx @vitejs/plugin-vue2-jsx

然后配置vite插件:

{ plugins: [ isVue3() ? vitePluginJsx({ include: [/\.[jt]sx$/] }) : vitePluginJsx2({ include: [/\.[jt]sx$/] }) ] } 5. 运行时兼容方案

我们知道Vue 2.7 和 Vue 3.x或多或少API上存在差异,上面也详细说过,那么怎么做到兼容两个版本API呢?

很简单,没有的API就自己实现,从而抹平差异,所以需要一层适配器。用下面这个图来表示,再适合不过了:

在适配器中,我们区分了两套代码,分别对应 Vue 3.x 和 Vue 2.7

image.png

如果是Vue3的代码,直接export导出就可以了,不用做polyfill

如果是Vue 2.7就需要单独实现代码了。

最后直接导出:

image.png

为什么这里,只导出vue3呢? 其实这里,在编译层面我们做了一些处理,如果是Vue 2.7版本,会自动修改为:

export * from './v2' 总结

由于是用于公司内部代码,所以并未开源,相关代码就不放出来了,以上只列出了核心代码和逻辑。其中需要处理的细节非常多,例如:

vue2 和 vue3的类型在同一套代码中怎么兼容 API适配,怎么实现? 组件打包后的产物怎么区分两种版本代码 组件库如何自动识别当前项目vue版本,做自动切换 ...

当然,这些问题其实方案都已经完善了,后续有空会考虑开发一套兼容2.7和3.x的组件库框架(如果有时间~。。)



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3