vue自定义指令

在vue中我们使用较多的v-ifv-showv-module这种以v- 开头的命令是vue自带的指令,通过vue这一套指令,我们可以更加友好的实现数据驱动视图 的操作。 当我们也有类似需求像将某个功能封装为自定义指令,让功能更加丰富和交互效果更加灵活,同时也能提高代码的可维护性和性能。比如在需要调用的地方如同v-if写在对应标签元素上,就直接实现这个功能。vue也考虑到了这一点,所有提出自定义指令的概念,这篇文档记录我学习vue自定义指令的理解。

使用Vue的自定义指令有以下几个好处:

  1. 扩展HTML功能:自定义指令能够扩展HTML元素的功能,通过为元素添加自定义行为和样式,使得开发者可以更灵活地控制和定制页面的交互效果。
  2. 代码复用:通过封装一些常用的DOM操作或特定的UI交互逻辑为自定义指令,可以在不同的组件中复用这些指令,减少重复的代码编写。
  3. 提高可维护性:通过将具体的DOM操作封装为自定义指令,可以将这部分逻辑与组件的其它部分隔离,提高代码的可维护性和可读性。
  4. 增强交互性:自定义指令可以方便地实现一些特定的交互效果,例如拖拽、滚动加载、表单验证等。通过自定义指令,可以将这些交互行为与业务逻辑解耦,使得代码更加清晰和易于维护。
  5. 更好的性能:通过自定义指令,可以直接操作DOM元素,而无需通过Vue的响应式机制来更新视图。在某些情况下,使用自定义指令可以提高应用的性能。

明白自定义组件的好处和作用,在页面中该如何使用

指令的使用

注册指令

自定义指令可以通过Vue实例的directive方法注册,注册指令有两种方式,一种是全局注册局部注册,面对不同场景下有不同选择,比如说注册一个点击和复制data值的组件采用全局注册最好,因为不止一个地方会调用。比如说,对一个页面按钮级别权限控制,肯定对那个页面组件有用,其他地方没啥用,采用局部注册最好。

全局注册

全局注册的自定义指令可以在整个应用程序中的任何组件中使用。通过Vue.directive方法可以将一个自定义指令注册为全局指令。然后,你可以在任何组件的模板中使用这个自定义指令。

js 复制代码
// 全局注册自定义指令 
Vue.directive('my-directive', { bind: function(el, binding, vnode) { 
// 指令绑定时的逻辑 
}, 
// 其他钩子函数 
});

局部注册

局部注册的自定义指令只能在某个组件内部使用。在组件的选项对象中,通过directives属性 来定义和注册自定义指令。

js 复制代码
// 局部注册自定义指令
export default {
  directives: {
    'my-directive': {
      bind: function(el, binding, vnode) {
        // 指令绑定时的逻辑
      },
      // 其他钩子函数
    }
  },
  // 组件的其他选项
}

指令生命周期

定义一个自定义指令需要指定以下几个属性:

  • bind:在指令第一次绑定到元素时调用,可以在这里进行一次性的设置。
  • inserted:被绑定的元素插入到父节点时调用。
  • update:被绑定元素的值更新时调用,可以根据新的值进行相应的操作。
  • componentUpdated:被绑定元素及其子元素的所有更新完成后调用。
  • unbind:指令与元素解绑时调用。

比如下例,在dircective声明一个自定义组件test 在页面中可以标签中写上v-test来调用指令。

html 复制代码
 <div v-test>点击test</div>
js 复制代码
          //局部注册
          directives:{
                test:{
                    bind:(el,binding)=>{
                        console.log("初始化");
                        console.log(el);
                        console.log(binding);
                    },
                    // 指令的定义
                    inserted:function(el,binding){
                        console.log("插入");
                        console.log(el);
                        console.log(binding);
                    },
                    update:function(el,binding){
                        console.log("更新");
                        console.log(el);
                        console.log(binding);
                    },
                    componentUpdated:function(el,binding){
                        console.log("更新完成");
                        console.log(el);
                        console.log(binding);
                    },
                    unbind:function(el,binding){
                        console.log("移除");
                        console.log(el);
                        console.log(binding);
                    }
                },
            },

使用场景

先放上我写的案例代码,下面将结合不同场景功能,在对应指令不同生命周期中如何使用。

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
    </style>
</head>
<body>
    <div id="app">
        <div v-test>
            点击test
        </div>
        <!-- 点击复制功能 -->
        <div v-copy="temp">
            点击hello world
        </div>
        <!-- 点击更新内容 -->
        <div v-event="temp">
            点击hello world 更改为 你好
        </div>
         <!-- 权限控制 -->
        <div>
            <input type="radio" v-model="radio" value="true" checked/>
            <input type="radio" v-model="radio" value="false"/>
        </div>
        <div >
            权限控制
        </div>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>  
        var app = new Vue({
            el:'#app',
            data:{
                temp:"hallo world",
                radio:true,
            },
            mounted(){
                
            },
            directives:{
                test:{
                    bind:(el,binding)=>{
                        console.log("初始化");
                        console.log(el);
                        console.log(binding);
                    },
                    // 指令的定义
                    inserted:function(el,binding){
                        console.log("插入");
                        console.log(el);
                        console.log(binding);
                    },
                    update:function(el,binding){
                        console.log("更新");
                        console.log(el);
                        console.log(binding);
                    },
                    componentUpdated:function(el,binding){
                        console.log("更新完成");
                        console.log(el);
                        console.log(binding);
                    },
                    unbind:function(el,binding){
                        console.log("移除");
                        console.log(el);
                        console.log(binding);
                    }
                },
            },
            mounted(){
                console.log("初始化完成");
                this.$on("longpressss",(val)=>{
                    console.log("抛出事件",val);
                    this.radio = val;
                });
            },
            methods:{
                
            }
        });
    </script>
</body>
</html>

点击复制

实现点击复制功能比较简单,注册了一个名为copy的指令。指令的bind钩子函数绑定了点击事件,并在点击时执行复制操作。通过binding.value可以获取到指令绑定的值,即要复制的文本内容。

然后,在模板中使用v-copy指令,将要复制的文本作为指令的值传入。当按钮被点击时,指令的bind钩子函数会被触发,从而执行复制操作。

需要注意的是,由于安全性限制,复制操作只能在用户主动触发的事件中执行,例如点击事件。而无法在页面加载或其他非用户交互事件中自动执行复制操作。

下面将简单用局部注册的方式进行演示:

js 复制代码
            directives:{
                test:{...之前代码},
                copy:{
                    bind:(el,binding)=>{
                        console.log("初始化");
                        //初始化给el添加点击事件
                        el.onclick = function(){
                            // 绑定事件
                            console.log("复制",binding,el.innerText);
                            var textarea = document.createElement("textarea");
                            textarea.value = binding.value;
                            document.body.appendChild(textarea);
                            textarea.select();
                            document.execCommand('copy');
                            textarea.remove();
                        }
                    },
                },
             }

在html地方给div标签添加v-copy,运行页面点击就可以看到复制data元素下temp的值hallo world

html 复制代码
        <!-- 点击复制功能 -->
        <div v-copy="temp">
            点击hello world
        </div>

说明下 为啥要添加textarea,不添加textarea元素可以用吗?

需要添加textarea元素的原因是,document.execCommand('copy')方法要求在执行复制操作之前,必须先选中要复制的文本内容。

通过创建一个临时的textarea元素,并将要复制的文本内容设置为其值,然后将该元素添加到DOM树中,可以成功选中并复制文本内容。这是因为textarea元素具有可选中文本的特性。

所以,为了在Vue中实现复制文本的自定义指令,需要添加临时的textarea元素来实现复制操作。如果直接在el上调用select()方法,复制操作将无法正常工作。

为啥取binding.value 而不是el.innerText?

在自定义指令中,取binding.value而不是el.innerText的原因是为了提供更灵活的使用方式。

当使用指令时,我们可以将任意表达式作为指令的值传递。这样,我们可以动态地从组件实例中获取数据、计算属性等,并将其作为要复制的文本内容。这种方式允许我们根据具体需求来决定要复制的文本内容,并且可以在组件使用该指令时进行动态更新。

如果直接使用el.innerText,则意味着我们只能复制指令所绑定元素的当前文本内容。这样会限制了复制的灵活性,并且无法动态更新要复制的文本内容。

通过使用binding.value,我们可以从指令的值中获取到所需的文本内容,而不受限于绑定元素的当前文本内容。这使得指令更具通用性和可扩展性。

指令事件抛出

先注入自定义指令v-event,在event指令中添加vnode属性,通过vnode.context.settemp("你好");抛出事件,指向调用的methods函数settemp,主要原因自定义指令本身不能直接修改组件实例中的数据。

Vue的自定义指令主要用于操作DOM元素,而不应该用于直接修改组件实例的数据。如果你需要修改组件实例中的数据,应该在指令中通过触发事件或调用方法来与组件通信,然后由组件来修改数据。 代码如下:

html 复制代码
        <!-- 点击更新内容 -->
        <div v-event="temp">
            点击hello world 更改为 你好
        </div>
js 复制代码
        directives:{
        //...之前代码
        copy:{
            bind:(el,binding)=>{//...之前代码}.
            update:function(el,binding){
                console.log("更新");
                //初始化给el添加点击事件
                el.onclick = function(){
                    // 绑定事件
                    console.log("复制",binding,el.innerText);
                    var textarea = document.createElement("textarea");
                    textarea.value = binding.value;
                    document.body.appendChild(textarea);
                    textarea.select();
                    document.execCommand('copy');
                    textarea.remove();
                }
            }
        }
        ,event:{
            bind:(el,binding,vnode)=>{
                console.log("初始化");
                //初始化给el添加点击事件
                el.onclick = function(){
                    // 事件抛出
                    vnode.context.settemp("你好");
                }
            }
       }
       //....其他代码
  methods:{
    //函数设置指令传入值
    settemp(str){
        console.log("设置值",str);
        this.temp = str;
    }
}

在上面的代码中,copy指令添加update事件,主要测试 event调用settemp(str)方法后修改temp值,那么对应copy指令的点击事件也需要跟着修改。

按钮级权限控制

这里简单写案例,通过一个单选按钮控制文字是否显示,作为一个引子,可以深入探讨理解。

html 复制代码
        <!-- 权限控制 -->
        <div>
            <input type="radio" v-model="radio" value="true" />
            <input type="radio" v-model="radio" value="false"/>
        </div>
        <div v-jurisdiction="radio">
            权限控制
        </div>
js 复制代码
        jurisdiction:{
            bind:()=>{
            },
            update:(el,binding)=>{
                if(binding.value == "true"){
                    el.style.display = "none";
                console.log(binding.value,"权限控制");
                }else{
                    el.style.display = "block";
                }
            },
        }

当用户点击input 对应属性radio发生改变,进而触发v-jurisdiction指令的update函数。 通过参数el获取到当前指令绑定的dom元素,通过对DOM属性进行操作来实现一些功能。例如上例修改style.display

其他案例推荐,可以参考学习这篇博客分享8个非常实用的Vue自定义指令 - 掘金 (juejin.cn)

相关推荐
喜欢踢足球的老罗16 分钟前
一张跨域图的“四次换乘“:blob URL 与 Chrome 扩展架构里的工程艺术
前端·chrome·架构
程序员黑豆17 分钟前
AI全栈开发 - Java:基本数据类型 vs 引用数据类型的内存存储
java·前端·ai编程
FserSuN18 分钟前
Chrome CORS / PNA / LNA 问题排查与解决方案
前端·chrome
小小小小宇26 分钟前
Claude Code 自动运行方法大全
前端
道友可好28 分钟前
AI 测试全绿,代码却是错的
前端·人工智能·后端
国科安芯1 小时前
商业航天通信载荷数字处理单元供电架构研究——基于ASP7A84AS的高精度低压差线性稳压器技术分析
前端·单片机·嵌入式硬件·fpga开发·架构·安全性测试
TangentDomain1 小时前
AI 写代码时代,游戏 UI 架构为什么停在 MVP?
前端·游戏·架构
英勇无比的消炎药1 小时前
前端提效神器全新AI组件库TinyRobot改写日常开发模式
前端·vue.js
GuWenyue1 小时前
10分钟搞定TodoList实战!从0搭建Bun+TS的RESTful接口服务
前端·typescript·bun