互动接收端 UI 组件如何兼容 Vue2 和 Vue3

互动接收端 SDK

保利威云直播产品能够在直播过程发动签到、抽奖、问卷等互动。对应地,在观看端也须要出现这些互动。为了让保利威云直播观看页以及客户定制观看页都能不便地接入这些性能,咱们把互动性能做成了一个 SDK,即互动接收端 SDK。

互动接收端 SDK 严格依照逻辑 与 UI 拆散的形式开发。基于逻辑层能够开发界面不同的 UI,甚至是不同技术栈的 UI。而默认的 UI 则是基于 Vue.js 2.6 开发的。

家喻户晓,Vue.js 3.x 曾经正式公布。如果把基于 Vue.js 2.6 开发的这套 UI 组件用于 Vue.js 3.x 的我的项目,会呈现报错;如果从新开发一套能适配 Vue.js 3.x 的 UI 组件,则须要保护两套代码,不利于后续性能迭代更新。

本文次要讲述如何在一份代码的根底上,构建出两个版本的 Vue.js 组件,从而适配 Vue.js 2.6 与 Vue.js 3.x(下文别离把 Vue.js 2.6 和 Vue.js 3.x 简称为 Vue2 和 Vue3)。

Vue2 和 Vue3 兼容架构介绍

在原有的代码根底上,别离编译构建出Vue2和Vue3的包。

  • 长处: 后续性能有新增或者优化,互动性能的源代码只须要改变一次。
  • 毛病: 无奈援用不兼容Vue2和Vue3的UI 组件库。 不能应用 Vue2、Vue3 不兼容的语法,包含 Vue3 新个性。

为UI组件我的项目搭建Vue3构建环境

原来UI组件我的项目目录构造如下:

  • 目录 ui

    • 目录 vue2

      • 目录 build
      • 目录 src
      • 文件 package.json
      • ..

为了搭建 Vue3 的构建环境,在ui目录下中新建一个vue3目录,并将vue2目录下除src以外的所有配置文件拷贝过去。目录构造如下:

  • 目录 ui

    • 目录 vue2

      • 目录 build
      • 目录 src
      • 文件package.json
      • ...
    • 目录 vue3

      • 目录 build
      • 文件 package.json
      • ...

接下来批改vue3目录下的配置文件,在package.json将vue更新到 3.1, 装置雷同版本的 @vue/compat@vue/compiler-sfc,并将vue-loader降级到16以上

"dependencies": {-  "vue": "^2.6.12",+  "vue": "^3.1.0",+  "@vue/compat": "^3.1.0",-  "vue-loader": "15.9.7"+  "vue-loader": "16.0.0"   ...},"devDependencies": {+  "@vue/compiler-sfc": "^3.1.0"}

在 Webpack 的构建配置中为 vue 设置别名 @vue/compat后,能够检测在Vue3中不兼容的代码片段。
设置webpack入口文件门路指向src目录下的源代码。

const srcDirname = path.resolve(__dirname, '../../vue2/src');module.exports = {    entry: path.join(srcPath, 'main.js'),    resolve: {        alias: {            vue: '@vue/compat',            '@': srcDirname        }    },    ...}

至此环境搭建结束。

解决代码兼容问题

运行我的项目后,关上demo页面仍有问题,并且控制台出现异常信息,须要修复这些问题,我的项目能力运行失常。

问题次要是因为以下起因导致的:(更多兼容问题参考官网文档 )

  • Vue2和Vue3别离通过 extend 和 createApp创立节点
  • 在Vue3中$options 属性不可批改
  • Vue3 不反对过滤器filter
  • Vue3 不反对2.6以下的插槽语法
  • Vue3 不反对全局办法$set
  • 生命周期destroy和beforeDestroy,在Vue3更名为unmounted和beforeUnmount

Vue2和Vue3别离通过 extend 和 createApp创立节点

对于无奈同时在Vue2和Vue3运行的代码,可通过判断版本号别离写两套代码

import Vue from 'vue';function isVue3() {    return Vue.version.startsWith('3.');}if(isVue3()) {    // Vue3 环境代码    const TipApp = Vue.createApp(SubmitTip, propsData);    TipApp.mount(wrapContain);    // 业务代码...} else {    // Vue2 环境代码    const Componet = Vue.extend(SubmitTip, propsData);    const TipApp = new Componet({        propsData    });    TipApp.$mount(wrapContain);    // 业务代码...}

在Vue3中$options 属性不可批改

起因:在Vue2中 $options能够动静批改,但在Vue3中$options属性只可读,不可批改。

解决方案: 将本来放在$options 的逻辑代码,迁徙到data()下。

// 原来的写法export default {  $i18n: null,  provide() {    // 初始化多语言实例    this.$options.$i18n = new I18n(this.$options.langs);    // 设置响应式语言属性    this.$options.$i18n.updateLocale(() => this.lang);    return {      getI18n: () => this.$options.$i18n    };  },  ...}// 代替写法export default {  data() {    return {      _i18n: null    }  }  provide() {    // 初始化多语言实例    this._i18n = new I18n(this.langs);    // 设置响应式语言属性    this._i18n.updateLocale(() => this.lang);    return {      getI18n: () => this._i18n,    };  },  ...}

Vue3 不反对过滤器filter

能够应用 methods 代替 filter。例如:

<!-- 过滤器filter写法 --> <div> {{ date  |  dateFilter }}</div><!-- methods写法 --> <div> {{ dateFilter(date) }}</div>

Vue3 不反对2.6以下的插槽语法

应用最新插槽语法能够在Vue2和Vue3同时运行。例如:

<!-- 废除语法--><template>    <template slot="header">      <h1> title </h1>    </template></template>
<!--2.6语法--><template>    <template v-slot:header>      <h1> title </h1>    </template></template>

Vue3 不反对全局办法$set

起因:在Vue3 $set 中曾经被移除。

解决方案:防止应用$set,能够在data()定义或者赋值对象和数组的时候,确保对象属性曾经响应式解决。

<!--谬误示例--><template>    <div v-for="(item,index) in list">        名字:{{ item.name }},        性别:{{ item.sex }},    </div></template><script>export default {    data() {        return {            list: []        }    },    mounted() {      this.list.push({name: '张三'})      // 在vue3中不失效      this.$set(this.list[0], 'sex', 'man');    }}</script>
<!--正确示例--><template>    <div v-for="(item,index) in list">        名字:{{ item.name }},        性别:{{ item.sex }},    </div></template><script>export default {    data() {        return {            list: []        }    },    mounted() {      this.list.push({name: '张三', sex: 'man'})    }}</script>

生命周期destroy和beforeDestroy,在Vue3更名为unmounted和beforeUnmount

通过版本判断应用不同的办法名。

export const BEFORE_DESTROY = isVue3() ? 'beforeUmount' : 'beforeDestory';export const DESTORY = isVue3() ? 'unmounted' : 'destory';
import { BEFORE_DESTROY } from '@/assets/utils/compat';export default {  [BEFORE_DESTROY]() {    // 业务代码...  }}