开发一个vue3插件,顺便兼容一下vue2咯

本文将介绍如何实现一个注册全局指令的插件,可以自动判断vue版本,并进行差异消除的处理。

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

前置介绍-出发点

最近写了个vue3的自定义指令,用来拖动元素。为了不影响元素的布局,选用了transform:translate()变换,来实现拖动。实现并不难,思路大概是:

  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. 先实现一下定义指令的对象:

    // 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中使用:

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

    <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方法。

    /* 
    在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. 插件定义

    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. 注册插件

    /*
    这里用伪代码,代不同环境
    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指令了,如果想测试一下,可安装以下依赖来玩一下:

    安装:

    npm i lite-move -S
    

    注册:

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

    使用:

    <template>
        <div v-move>可用鼠标拖动</div>
    </template>
    

结论

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

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

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

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

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

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

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

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值