vue中的自定义指令

269 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第2天,点击查看活动详情

前言

在项目中我们最常见的指令有很多,比如 v-if v-show ......,但是有些时候我们的组件中有些功能是重复的,我们完全可以自定义指令(小项目就算了,不至于),今天我们就来简单的聊聊自定义指令。

vue2中创建指令

vue2中提供了api(directive)供我们使用。

自定义指令: 输入框的值大于7时为红色,如果小于7则显示绿色

基本代码如下:

Vue.directive('shownumber', {
    bind: function (el, binding, vnode) {
        // console.log(el,binding,vnode)
        console.log(binding.value);
        console.log(vnode)
        if(binding.value>7){
            el.style.color='red';
        }else {
            el.style.color='green';
        }
    },
    inserted:function (a,b,c){
        console.log(a,b,c)
    },
    update: function (el,binding,vnnode) {
        console.log(el,binding)

    },
    componentUpdated: function (el,binding,vnnode) {
        if(binding.value>7){
            el.style.color='red';
        }else {
            el.style.color='green';
        }
    }
});

自定义指令中的特定函数:

  1. bind: 指令第一次绑定到元素上时调用,相当于初始化某个节点
  2. instead: 被绑定元素插入父节点时调用,这句话是什么意思呢,就是当前的节点插入的对象并不一定是文本,只是引入了到父级内。
  3. update: 当其所在的Vnode更新时进行调用
  4. componentUpdate: 指令所在的组件全部更新后调用
  5. unbind:解绑,只调用一次

注释:

el: 就是指当前的dom节点了

binding: 一个对象,最主要的有两点:

  1. value: 指令绑定的值
  2. oldvalue: 绑定前的值

注意:自定义指令最好不要用箭头函数,这样你就失去了传参的机会。

页面中使用指令

代码如下:

<template>
  <div style="padding-top: 100px;height: 250px">
    <input v-shownumber="input" v-model="input" placeholder="请输入内容"/>
    <el-button @click="input++">增加</el-button>
  </div>
</template>
<script>
export default {
  data () {
    return {
      input:5
    }
  },
}
</script>

效果如下:

当数字小于7时:

1681991851442.png

当数字大于7时:

image.png

指令在el-input中失效

失效原因: 我们在原生中绑定的就是当前的dom节点,也就是input,但是我们在使用elementui插件时,我们添加的一些属性可以覆盖掉input的属性,但是一些属性是覆盖不掉的,比如color

原生的input就是一个简单的input,但是el_input的真实节点中套了一层div:

image.png

解决办法: 找到子集,直接对子集进行修改,代码调整如下:

Vue.directive('shownumber', {
    bind: function (el, binding, vnode) {
        if (binding.value > 7) {
                setTimeout(()=>{
                    const nodes  = el.getElementsByTagName('input')[0]
                    nodes.style.color = 'red';
                    nodes.style.fontSize = '50px';
                },100)
        } else {
            setTimeout(()=>{
                const nodes  = el.getElementsByTagName('input')[0]
                nodes.style.color = 'green';
                nodes.style.fontSize = '50px';
            },100)

        }
    },
    inserted:function (a,b,c){
        console.log(a,b,c)
    },
    update: function (el,binding,vnnode) {
        if (binding.value > 7) {
            setTimeout(()=>{
                const nodes  = el.getElementsByTagName('input')[0]
                nodes.style.color = 'red';
                nodes.style.fontSize = '50px';
            },100)
        } else {
            setTimeout(()=>{
                const nodes  = el.getElementsByTagName('input')[0]
                nodes.style.color = 'green';
                nodes.style.fontSize = '50px';
            },100)

        }

    },
    componentUpdated: function (el,binding,vnnode) {
        if (binding.value > 7) {
            setTimeout(()=>{
                const nodes  = el.getElementsByTagName('input')[0]
                nodes.style.color = 'red';
                nodes.style.fontSize = '50px';
            },100)
        } else {
            setTimeout(()=>{
                const nodes  = el.getElementsByTagName('input')[0]
                nodes.style.color = 'green';
                nodes.style.fontSize = '50px';
            },100)

        }
    }
});
<template>
  <div style="padding-top: 100px;height: 250px">
    <el-input v-shownumber="input" v-model="input" placeholder="请输入内容"></el-input>
    <el-button @click="input++">增加</el-button>
  </div>
</template>
<script>
export default {
  data () {
    return {
      input:5
    }
  },
}
</script>

效果如下:

image.png

vue3中的自定义指令

其实vue3中的指令与vue2中的指令大差不差,我们可以按照模板来搞下:

import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router  from "./router";
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'


const app = createApp(App);
app.use(ElementPlus);
app.use(router);

// 自定义指令
app.directive('showNumber', {
    // 在绑定元素的 attribute 或事件监听器被应用之前调用, 在指令需要附加须要在普通的 v-on 事件监听器前调用的事件监听器时,这很有用
    created(el, binding) {},
    // 当指令第一次绑定到元素并且在挂载父组件之前调用
    beforeMount(el, binding) {},
    // 在绑定元素的父组件被挂载后调用
    mounted(el, binding) {},
    // 在更新包含组件的 VNode 之前调用
    beforeUpdate(el, binding) {},
    // 在包含组件的 VNode 及其子组件的 VNode 更新后调用
    updated(el, binding) {},
    // 在卸载绑定元素的父组件之前调用
    beforeUnmount(el, binding) {},
    // 当指令与元素解除绑定且父组件已卸载时, 只调用一次
    unmounted(el, binding) {},
});

不得不说,vue3中的函数比vue2看起来更加的直观一些,就像vue2中的生命周期一样。

// 自定义指令
app.directive('showNumber', {
    // 在绑定元素的 attribute 或事件监听器被应用之前调用, 在指令需要附加须要在普通的 v-on 事件监听器前调用的事件监听器时,这很有用
    created(el, binding) {},
    // 当指令第一次绑定到元素并且在挂载父组件之前调用
    beforeMount(el, binding) {
        if(binding.value<7){
            el.style.color ='green'
        }else {
            el.style.color ='red'
        }
    },
    // 在绑定元素的父组件被挂载后调用
    mounted(el, binding) {},
    // 在更新包含组件的 VNode 之前调用
    beforeUpdate(el, binding) {},
    // 在包含组件的 VNode 及其子组件的 VNode 更新后调用
    updated(el, binding) {
        if(binding.value<7){
            el.style.color ='green'
        }else {
            el.style.color ='red'
        }
    },
    // 在卸载绑定元素的父组件之前调用
    beforeUnmount(el, binding) {},
    // 当指令与元素解除绑定且父组件已卸载时, 只调用一次
    unmounted(el, binding) {},
});
<input v-showNumber="val1" v-model="val1">
<el-button @click="val1++">++</el-button>

效果如下:

image.png

image.png

同样的,在elementui中一样对节点的子集进行css的修改才能达到预期的效果。