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)

相关推荐
开心工作室_kaic32 分钟前
ssm068海鲜自助餐厅系统+vue(论文+源码)_kaic
前端·javascript·vue.js
有梦想的刺儿1 小时前
webWorker基本用法
前端·javascript·vue.js
cy玩具1 小时前
点击评论详情,跳到评论页面,携带对象参数写法:
前端
customer082 小时前
【开源免费】基于SpringBoot+Vue.JS周边产品销售网站(JAVA毕业设计)
java·vue.js·spring boot·后端·spring cloud·java-ee·开源
qq_390161772 小时前
防抖函数--应用场景及示例
前端·javascript
John.liu_Test2 小时前
js下载excel示例demo
前端·javascript·excel
Yaml43 小时前
智能化健身房管理:Spring Boot与Vue的创新解决方案
前端·spring boot·后端·mysql·vue·健身房管理
PleaSure乐事3 小时前
【React.js】AntDesignPro左侧菜单栏栏目名称不显示的解决方案
前端·javascript·react.js·前端框架·webstorm·antdesignpro
哟哟耶耶3 小时前
js-将JavaScript对象或值转换为JSON字符串 JSON.stringify(this.SelectDataListCourse)
前端·javascript·json
getaxiosluo3 小时前
react jsx基本语法,脚手架,父子传参,refs等详解
前端·vue.js·react.js·前端框架·hook·jsx