开发一个vue3插件,兼容vue2可能吗

当你点进来,这个问题就有了答案,可以的! 本文将介绍如何实现一个注册全局指令的插件,可以自动判断vue版本,并进行差异消除的处理。

本文示例源码:giteegithubnpm地址,欢迎start哦。

前置介绍-出发点

最近写了个vue3的自定义指令,用来拖动元素。为了不影响元素的布局,选用了transform:translate()变换,来实现拖动(感觉比烂大街的修改top和left更合适有木有)。实现并不难,思路大概是:

  1. 监听元素mousedown事件,点击时记录开始坐标。并监听元素mousemove事件。
  2. 当元素触发mousemove,实时获取鼠标坐标,并减去第一步记录的开始坐标,得到移动的距离。
  3. 得到了移动距离,实时更新到translate即可。
  4. 监听mouseup、mouseleave事件,移除mousemove还有自己的监听,收尾!

指令实现起来挺简单,考虑到目前vue3还没火到完全取代vue2,我想能不能让它在vue2也正常运行呢?

具体实现可跳转github查看哦,这里主要介绍如何实现兼容。

行动

实现一个兼容vue2、vue3的指令

准备:通过官网介绍,对比vue2和vue3实现自定义指令的异同:

  • vue2、vue3中的自定义指令都是一个对象,对象里是各种钩子函数,形式上一致。
  • 每个钩子接收的参数个数、位置,vue2、vue3几乎没有区别,只是第二个参数(一个对象)里有几个不常用属性的进行了破坏性更新,常用的没变。
  • vue3中的钩子更多更精细,且覆盖了vue2的所有钩子,只是命名不一样(vue2的那几个也够用了)。

结论:

完全具有可行性!

只要将定义指令的对象,定义为一个模块,内部实现的逻辑只需要一份,只是导出vue3的指令对象的同时,再适配vue2,把钩子名换成vue2的命名方式,再导出一个对象即可。

基本实现:

  1. 先实现一下定义指令的对象:

    js 复制代码
    // lite-move.js
    // 拖动处理函数
    function moveAction(e) { /* e为事件对象,也可以通过this来访问dom元素 */ }
    
    // 元素插入dom时的钩子函数
    const mounted = (el, binding) => {
        // moveAction内部要访问dom时,最好用this,而不是钩子提供的el,性能更好,耦合度低
        el.addEventListener('mousedown', binding.value = moveAction)
    }
    
    // 父组件销毁时的钩子函数
    const unmounted = (el, binding) => {
        el.removeEventListener('mousedown', binding.value)
    }
    
    // vue3中定义指令的对象
    export const vMove = {
        mounted,
        unmounted
    }
    
    // vue2中定义指令的对象,改一下属性名即可
    export const vMoveFor2 = {
        inserted: mounted,
        unbind: unmounted
    }
  2. 在vue3中使用:

    html 复制代码
    <template>
      <div ref="moveEle" v-move>
        <span>移动</span>
      </div>
    </template>
    
    <script setup>
    import { moveDirective } from 'lite-move'; // 导入符合vue3语法的指令对象
    // 注册局部指令
    const vMove = moveDirective;
    </script>
  3. 在vue2中使用:

    html 复制代码
    <template>
        <div v-move>移动</div>
    </template>
    
    <script>
    import { moveDirectiveFor2 } from 'lite-move';
    export default {
        directives: {
            // 注册局部指令
            move: moveDirectiveFor2
        }
    }
    </script>

通过插件实现自动判断vue版本

上面这种注册指令的方式还是有点麻烦了,还需要导入对应的对象才行。为了能实现一种写法,可以在两种环境中运行,还需要一个中间过程:vue插件。

为什么时vue插件呢

  • vue3和vue2的插件形式、注册方式都相同。

    vue3、vue2都是通过use来注册插件,并且都会调用插件的install方法。

    js 复制代码
    /* 
    在vue2中,注册插件的对象是Vue构造函数
    在vue3中,注册插件的对象是createApp返回的对象
    用伪代码演示:
    
    if vue2: import app from 'vue';
    if vue3: import {createApp} from 'vue'; const app = createApp();
    
    */
    import { moveDirectivePlugin } from 'move-plugin'
    app.use(moveDirectivePlugin)
  • (关键)vue3和vue2调用use来注册插件时,传递给install方法的第一个参数,虽然有本质的区别,但有共同的属性:

    1. 都可以获取第一个参数上的version属性,取得当前vue版本。
    2. 都可以通过第一个参数上的directive方法,注册全局指令,并且参数一样。

实现

  1. 插件定义

    js 复制代码
    import { moveDirectiveFor2, moveDirective } from 'lite-move';
    export const moveDirectivePlugin = {
        install(app) {
            // 如果是vue2,version值为'2';如果是vue3,则为'3'
            const version = app.version.charAt(0);
    
            // 根据vue版本作不同的处理,例如使用不同的指令对象,注册指令
            if (version === '2') {
                // @ts-ignore
                app.directive('move', moveDirectiveFor2)
            }
            if (version === '3') {
                app.directive('move', moveDirective)
            }
        }
    }
  2. 注册插件

    js 复制代码
    /*
    这里用伪代码,代不同环境
    if vue2: import app from 'vue';
    if vue3: import {createApp} from 'vue'; const app = createApp();
    */
    import { moveDirectivePlugin } from 'move-plugin';
    app.use(moveDirectivePlugin);
  3. 最后就可以在全局访问上面注册的v-move指令了,如果想测试一下,可安装以下依赖来玩一下:

    安装:

    shell 复制代码
    npm i lite-move -S

    注册:

    js 复制代码
    /*
    这里用伪代码,代不同环境
    if vue2: import app from 'vue';
    if vue3: import {createApp} from 'vue'; const app = createApp();
    */
    import { moveDirectivePlugin } from 'lite-move';
    app.use(moveDirectivePlugin);

    使用:

    html 复制代码
    <template>
        <div v-move>可用鼠标拖动</div>
    </template>

结论

vue3虽然对vue2进行了很多更新,包括很多破坏性更新,但有不少功能的设计还是有vue2的影子。正因为这点,让设计一个兼容vue2的vue3插件具备可行性,一些个人实践心得:

  • 由于vue2的影响力,vue3也将会是趋势,即使目前很多公司还没用起来,自己在社区多捣鼓一下是有必要的。

  • 写一些兼容二者的插件、指令,可进可退。

  • 实现一个兼容性好的插件,必须考虑到耦合性。

    例如上面的情景中:在监听拖动的事件函数内部,它可以获取外部作用域的el来获取dom,也可以使用自身this来获取,通过自身this来获取耦合性明显更低。

  • 实现思路主要是,先考虑该功能依赖了vue的哪些api,这些api在vue2和vue3中有哪些不同。优先使用一样的api,其次是只是名字不同,但功能一样的api。最后,如果实在不同,就需要考虑可不可以换成其它方式来消除差异。

  • 可以通过注册vue插件方式来隐藏版本之间差异,如判断版本,进行不同的处理,保证使用上的一致性。

  • 虽然答案是可以,但还是有一定局限性的,像注册全局指令、在vue实例上挂载公共方法等,通过注册插件来实现还是很简单的。但如果是注册组件,限制就非常多了。

相关推荐
ziyue75751 小时前
vue修改element-ui的默认的class
前端·vue.js·ui
程序定小飞3 小时前
基于springboot的在线商城系统设计与开发
java·数据库·vue.js·spring boot·后端
BumBle4 小时前
uniapp 用css实现圆形进度条组件
前端·vue.js·uni-app
Komorebi_99994 小时前
Vue3 + TypeScript provide/inject 小白学习笔记
前端·javascript·vue.js
二十雨辰5 小时前
vite性能优化
前端·vue.js
明月与玄武5 小时前
浅谈 富文本编辑器
前端·javascript·vue.js
FuckPatience6 小时前
Vue 与.Net Core WebApi交互时路由初探
前端·javascript·vue.js
aklry7 小时前
elpis之学习总结
前端·vue.js
FuckPatience8 小时前
Vue ASP.Net Core WebApi 前后端传参
前端·javascript·vue.js
Komorebi_99999 小时前
Vue3 provide/inject 详细组件关系说明
前端·javascript·vue.js