一、简介

vue除了提供了默认内置的指令外 (例如:v-model 和 v-show),还允许开发人员根据实际情况自定义指令,它的作用价值在于当开发人员在某些场景下需要对普通DOM元素进行操作的时候。


二、使用

Vue自定义指令和组件一样存在着 全局注册局部注册 两种方式。

1. 全局注册

注册全局指令的方式,是通过 Vue.directive( id, [definition] ) 方法:

  • 第一个参数 id :自定义指令名称;
    注意:指令名称不需要加 v- 前缀,默认是自动加上前缀的,使用指令的时候一定要加上前缀。
  • 第二个参数:可以是对象数据,也可以是一个指令函数。
<template>
	<div id="app">
		<-- v-focus为自定义指令 -->
    	<input type="text" placeholder="我是全局自定义指令" v-focus>
	</div>
</template>

<script>
	// 注册全局指令
    Vue.directive("focus", {
        inserted(el){
            el.focus();
        }
    })
    
    new Vue({
        el: "#app"
    })
</script>

2. 局部注册

通过在vue实例中添加 directives 对象数据,可以注册局部自定义指令。

我们还是以全局注册的案例为例,通过注册一个 v-focus 指令,实现了在页面加载完成之后自动让输入框获取到焦点的小功能。其中 inserted 是自定义指令的钩子函数,后面的内容会详细讲解。

<template>
  <div>
    <input type="text" placeholder="我是局部自定义指令" v-focus>
  </div>
</template>

<script>
export default {
  name: 'index',
  directives: {
    focus: {
      //自定义指令的钩子函数:
      inserted (el, binding, vnode) {
        el.focus()
      }
    }
  }
}
</script>

页面效果:
在这里插入图片描述

三、钩子函数

一个指令定义对象可以提供如下几个钩子函数 (均为可选):

  • bind:只调用一次,指令 第一次绑定到元素时 调用。在这里可以进行一次性的初始化设置;
  • inserted:被绑定元素 插入父节点时 调用 (仅保证父节点存在,但不一定已被插入文档中);
  • update:所在组件的 VNode 更新时 调用,但是可能发生在其子 VNode更新之前。指令的值可能发生了改变,也可能没有;
  • componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后 调用;
  • unbind:只调用一次,指令与元素 解绑时 调用。

四、钩子函数参数

指令钩子函数会被传入以下参数:

  • el:指令所绑定的元素,可以用来直接操作 DOM;

  • binding:一个对象,包含以下 property:

  • name指令名,不包括 v- 前缀;

  • value指令的绑定值,例如:v-focus=“1 + 1” 中,value为 2;

  • oldValue指令绑定的前一个值,仅在 updatecomponentUpdated 钩子中可用(无论值是否改变都可用);

  • expression:字符串形式的指令表达式,例如 v-focus=“1 + 1” 中,表达式为 “1 + 1”。

  • arg:传给指令的参数,可选。例如 v-focus:message 中,参数为 “message”;

  • modifiers:一个包含修饰符的对象。例如:v-focus.a.b 中,修饰符对象为 {a: true,b: true}。

  • vnode:Vue 编译生成的 虚拟节点

在这里插入图片描述

  • oldVnode:上一个虚拟节点,仅在 updatecomponentUpdated 钩子中可用。

注意:除了 el 之外,其它参数都应该是只读的,切勿进行修改。如果需要在钩子之间共享数据,建议通过元素的 dataset 来进行。

<input type="text" placeholder="我是局部自定义指令" v-focus:message.a.b="1+1">
directives: {
    focus: {
      inserted (el, binding, vnode) {
        el.focus()
        console.log(el)
        console.log(binding.name)
        console.log(binding.value)
        console.log(binding.arg)
        console.log(binding.expression)
        console.log(binding.modifiers)
        console.log(vnode)
      }
    }
  }

在这里插入图片描述
动态指令参数
指令的参数可以是动态的。例如,在 v-mydirective:[argument]=“value” 中,argument 参数可以根据组件实例数据进行更新。

例如:我们想要创建一个通过指令值来更新竖直位置像素值的自定义指令:

<template>
	<div class="box" v-pin="200"></div>
</template>

<script>
export default {
  name: 'index',
  directives: {
    pin: {
      bind (el, binding) {
        el.style.position = 'fixed'
        el.style.top = binding.value + 'px'
      }
    }
  }
}
</script>

页面效果:
在这里插入图片描述
这样就可以把该元素固定在距离页面顶部 200 像素的位置,但是如果想把元素固定在左侧而不是顶部又该怎么办呢?这时使用动态参数就可以非常方便地根据每个组件实例来进行更新。

<template>
	<div class="box" v-pin:[direction]="200"></div>
</template>

<script>
export default {
  name: 'Directive',
  data () {
    return {
      direction: 'left'
    }
  },
  directives: {
    pin: {
      bind (el, binding) {
        el.style.position = 'fixed'
        let direction = binding.arg ? binding.arg : 'left'
        el.style[direction] = binding.value + 'px'
      }
    }
  }
}
</script>

在这里插入图片描述
这样这个自定义指令现在的灵活性就足以支持一些不同的用例了。

五、函数简写

在很多时候,我们可能只想在 bindupdate 时触发相同行为,而不关心其它的钩子。
可以将自定义指令这样简写:

  directives: {
    pin (el, binding) {
      el.style.position = 'fixed'
      el.style[binding.arg] = binding.value + 'px'
    }
  }

六、对象字面量

如果指令需要多个值,可以传入一个 JavaScript 对象字面量。
指令函数能够接受所有合法的 JavaScript 表达式。

<template>
  <div class="box" v-pin="{color:'red',text: 'box'}"></div>
</template>

<script>
export default {
  name: 'Directive',
  data () {
    return {
      direction: 'left'
    }
  },
  directives: {
    pin (binding) {
      console.log(binding.value.color) // red
      console.log(binding.value.text)  // box
    }
  }
}
</script>

七、批量注册指令

  1. 新建 directives 文件夹,将所有自定义指令的 js 文件全部放到这里:
    在这里插入图片描述

focus.js

export default {
  inserted (el) {
    el.focus()
  }
}
  1. directives 文件中新建 index.js ,引入所有的自定义指令文件,并进行 全局的批量注册

index.js

import focus from '@/directives/focus'
import copy from '@/directives/copy'
// 自定义指令
const directives = {
  focus,
  copy
}

export default {
  install (Vue) {
    Object.keys(directives).forEach((key) => {
      Vue.directive(key, directives[key])
    })
  }
}
  1. main.js 中引入 index.js 并调用:

main.js

import Vue from 'vue'
import App from './App'
import router from './router'
// 引入自定义指令
import Directives from '@/directives/index.js'
Vue.use(Directives)

Vue.config.productionTip = false

new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>'
})
  1. 然后你在页面的任何地方就可以直接使用这些自定义指令了:

helloWorld.vue

<input type="text" v-focus>

页面效果
在这里插入图片描述


Logo

前往低代码交流专区

更多推荐