开发一个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实例上挂载公共方法等,通过注册插件来实现还是很简单的。但如果是注册组件,限制就非常多了。

相关推荐
Ashore_1 小时前
从简单封装到数据响应:Vue如何引领开发新模式❓❗️
前端·vue.js
顽疲1 小时前
从零用java实现 小红书 springboot vue uniapp (6)用户登录鉴权及发布笔记
java·vue.js·spring boot·uni-app
&活在当下&1 小时前
ref 和 reactive 的用法和区别
前端·javascript·vue.js
云白冰2 小时前
hiprint结合vue2项目实现静默打印详细使用步骤
前端·javascript·vue.js
m0_748251723 小时前
前端入门之VUE--ajax、vuex、router,最后的前端总结
前端·vue.js·ajax
customer083 小时前
【开源免费】基于SpringBoot+Vue.JS安康旅游网站(JAVA毕业设计)
java·vue.js·spring boot·后端·kafka·开源·旅游
跨境商城搭建开发5 小时前
一个服务器可以搭建几个网站?搭建一个网站的流程介绍
运维·服务器·前端·vue.js·mysql·npm·php
hhzz5 小时前
vue前端项目中实现电子签名功能(附完整源码)
前端·javascript·vue.js
slongzhang_6 小时前
elementPlus消息组件多按钮案例
前端·javascript·vue.js
会发光的猪。7 小时前
vue中el-select选择框带搜索和输入,根据用户输入的值显示下拉列表
前端·javascript·vue.js·elementui