前言

什么是vue指令了?
Vue自带的指令很多,v-for/v-if/v-else/v-else-if/v-model/v-bind/v-on/v-show/v-html/v-text…
原来这就是指令。

Vue如何创建自定义指令?

但是这些指令都是比较偏向于工具化,有些时候在实现具体的业务逻辑的时候,发现不够用,如何来自定义指令?

全局指令

第一步

最好建立一个全局的命令文件例如:directive.js
利用Vue.directive()建立一个全局命令,并将它暴露出来,例如一个focus 让表单自动聚焦

// directive.js import Vue from 'vue'; const focus = Vue.directive('focus', { // 指令的钩子函数--第一次绑定元素时调用 bind(el) { console.warn('指令的钩子函数:bind'); console.log(el); }, /** * inserted 在元素被插入到页面中的时候调用---vm实例的钩子函数 * binding是一个对象,包括指令的所有信息 */ inserted: function (el, binding) { el.focus(); el.setAttribute('placeholder', 'web秀'); // 自动聚焦,input提示placeholder输入 "web秀" // 其中foo是指令参数--可以打印看看 console.log(binding.arg); }, // 指令的钩子函数,只调用一次,指令与元素解绑时调用(即当标签被删除时)。 unbind() { // 当指令所在的元素,从页面中移除的时候,unbind钩子函数会被执行 console.warn('指令的钩子函数:unbind'); } }) export {focus}

第二步

在main.js(入口JS文件)中将它引入,可以省略文件后缀

// main.js import focus from 'xxx/directive'

这样任何一个Vue文件只有使用v-focus(指令名),就可以很方便的自动聚焦了。

<input type="text" v-focus:foo="我是指令参数">

局部指令

用法和全局指令一样,只是在单个Vue实例页面内部定义,只能被这一个实例使用,而全局可以被多个实例使用。

<div> <input type="text" v-focus:foo="我是指令参数"> </div> <script> var vm = new Vue({ el: '#app', data: { }, directives:{ focus:{ // 创建局部指令 inserted: function (el, binding) { el.focus(); el.setAttribute('placeholder', 'web秀'); }) } } }) </script>

自定义指令钩子函数参数介绍

函数

  1. bind:只调用一次,指令第一次绑定到元素时调用,用这个钩子函数可以定义一个在绑定时执行一次的初始化动作。
  2. inserted:被绑定元素插入父节点时调用 (父节点存在即可调用,不必存在于 document 中)。
  3. update:所在组件的 VNode 更新时调用,但是可能发生在其孩子的 VNode 更新之前。指令的值可能发生了改变也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。
  4. componentUpdated:所在组件的 VNode 及其孩子的 VNode 全部更新时调用。
  5. unbind:只调用一次,指令与元素解绑时调用。

参数

  1. el:指令所绑定的元素,可以用来直接操作 DOM 。
  2. binding:一个对象,包含以下属性:
    **name:**指令名,不包括 v- 前缀。
    **value:**指令的绑定值,例如:v-my-directive=“1 + 1”, value 的值是 2。oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。
    **expression:**绑定值的字符串形式。例如 v-my-directive=“1 + 1” ,expression 的值是 “1 + 1”。
    **arg:**传给指令的参数。例如 v-my-directive:foo,arg 的值是 “foo”。
    **modifiers:**一个包含修饰符的对象。例如:v-my-directive.foo.bar, 修饰符对象 modifiers 的值是 { foo: true, bar: true }。
  3. vnode:Vue 编译生成的虚拟节点,查阅 VNode API 了解更多详情。
  4. oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。

学以致用

最后我们来写一个非常实用的栗子,页面倒计时,请上眼

function padZero(number) { let tmp = "0" + number; return tmp.slice(-2, tmp.length); } function countDown(diff) { // 剩余毫秒 let millisecond = diff % 1000; // 总秒 let seconds = (diff - millisecond) / 1000; // 剩余秒数 let realseconds = seconds % 60; // 剩余分钟数 let minutes = (seconds - realseconds) / 60; // let realminutes = seconds % 60; // let hours = (diff - minutes - seconds) % 86400; return diff >= 0 ? padZero(minutes) + "分" + padZero(realseconds) + "秒" : ""; } ... directives: { timer: { inserted(el, binding, vnode) { const me = vnode.context; const flight = me.$store.state.flight; // 获取参数,截止时间与当前时间差(单位毫秒) let val = +binding.value; let timer = window.setInterval(() => { val -= 1000; // 每秒减1000毫秒/1秒 let instance = countDown(val); if (!instance) { // 倒计时结束,do something if (timer) { window.clearInterval(timer); timer = null; } return; } // 格式化的日期插入到页面 el.innerHTML = instance; }, 1000); } } } ...

页面可以直接使用

<!-- 截止时间与当前时间差(单位毫秒) --> <span v-timer='100000'></span>