钩子参数

钩子函数接收四个参数:el、binding、vnode 和 oldVnode。其中 binding 对象包含了指令的大部分信息。

参数列表

Vue.directive('my-directive', {
  bind(el, binding, vnode, oldVnode) {
    // el: 指令绑定的元素
    // binding: 包含指令信息的对象
    // vnode: Vue 编译生成的虚拟节点
    // oldVnode: 上一个虚拟节点(仅在 update 和 componentUpdated 中可用)
  }
})

el 参数

el 是指令绑定的 DOM 元素,可以直接操作:

Vue.directive('color', {
  bind(el, binding) {
    el.style.color = binding.value
    el.classList.add('highlight')
    el.setAttribute('data-custom', 'true')
  }
})

binding 对象

binding 对象包含以下属性:

name

指令名称,不包含 v- 前缀:

Vue.directive('my-directive', {
  bind(el, binding) {
    console.log(binding.name)  // 'my-directive'
  }
})

value

指令的绑定值:

<div v-demo="1 + 1"></div>

<script>
Vue.directive('demo', {
  bind(el, binding) {
    console.log(binding.value)  // 2
  }
})
</script>

oldValue

指令绑定的旧值,仅在 update 和 componentUpdated 中可用:

Vue.directive('demo', {
  update(el, binding) {
    if (binding.value !== binding.oldValue) {
      console.log('值已改变')
    }
  }
})

expression

字符串形式的指令表达式:

<div v-demo="1 + 1"></div>

<script>
Vue.directive('demo', {
  bind(el, binding) {
    console.log(binding.expression)  // '1 + 1'
  }
})
</script>

arg

传给指令的参数:

<div v-demo:foo="message"></div>

<script>
Vue.directive('demo', {
  bind(el, binding) {
    console.log(binding.arg)  // 'foo'
  }
})
</script>

modifiers

包含修饰符的对象:

<div v-demo.foo.bar="message"></div>

<script>
Vue.directive('demo', {
  bind(el, binding) {
    console.log(binding.modifiers)  // { foo: true, bar: true }
  }
})
</script>

完整示例

<div id="app">
  <div v-demo:foo.bar="message"></div>
</div>

<script>
Vue.directive('demo', {
  bind(el, binding) {
    console.log('name:', binding.name)           // 'demo'
    console.log('value:', binding.value)         // 'Hello Vue!'
    console.log('expression:', binding.expression) // 'message'
    console.log('arg:', binding.arg)             // 'foo'
    console.log('modifiers:', binding.modifiers)  // { bar: true }
  }
})

new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue!'
  }
})
</script>

动态参数

Vue 2.6+ 支持动态指令参数:

<div v-mydirective:[direction]="value"></div>

<script>
new Vue({
  el: '#app',
  data: {
    direction: 'left',
    value: '100px'
  }
})

Vue.directive('mydirective', {
  bind(el, binding) {
    const direction = binding.arg  // 'left'
    const value = binding.value    // '100px'
    
    el.style[direction] = value
  }
})
</script>

对象字面量

如果指令需要多个值,可以传入对象:

<div v-demo="{ color: 'red', size: '20px' }"></div>

<script>
Vue.directive('demo', {
  bind(el, binding) {
    const { color, size } = binding.value
    
    el.style.color = color
    el.style.fontSize = size
  }
})
</script>

vnode 参数

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

Vue.directive('demo', {
  bind(el, binding, vnode) {
    console.log(vnode.context)      // 组件上下文
    console.log(vnode.elm)          // 真实 DOM 元素
    console.log(vnode.data)         // VNode 数据
    console.log(vnode.componentOptions)  // 组件选项
  }
})

实际应用示例

带参数的颜色指令

<div id="app">
  <p v-color:text="'red'">文本颜色</p>
  <p v-color:bg="'blue'">背景颜色</p>
</div>

<script>
Vue.directive('color', {
  bind(el, binding) {
    const type = binding.arg
    const color = binding.value
    
    if (type === 'text') {
      el.style.color = color
    } else if (type === 'bg') {
      el.style.backgroundColor = color
    }
  }
})
</script>

带修饰符的权限指令

<div id="app">
  <button v-permission:edit="hasPermission">编辑</button>
  <button v-permission.delete="hasPermission">删除</button>
  <button v-permission:admin.disable="hasPermission">管理</button>
</div>

<script>
Vue.directive('permission', {
  bind(el, binding) {
    const action = binding.arg
    const modifiers = binding.modifiers
    const hasPermission = binding.value
    
    if (!hasPermission) {
      if (modifiers.disable) {
        el.disabled = true
        el.style.opacity = '0.5'
      } else {
        el.style.display = 'none'
      }
    }
  }
})

new Vue({
  el: '#app',
  data: {
    hasPermission: false
  }
})
</script>

防抖指令(带参数)

<div id="app">
  <input v-debounce:500="search" placeholder="搜索...">
  <input v-debounce:1000.immediate="search" placeholder="立即执行">
</div>

<script>
Vue.directive('debounce', {
  bind(el, binding) {
    const delay = parseInt(binding.arg) || 300
    const immediate = binding.modifiers.immediate
    
    let timer = null
    
    el._debounce = function(event) {
      if (immediate && !timer) {
        binding.value(event)
      }
      
      if (timer) clearTimeout(timer)
      
      timer = setTimeout(() => {
        if (!immediate) {
          binding.value(event)
        }
        timer = null
      }, delay)
    }
    
    el.addEventListener('input', el._debounce)
  },
  unbind(el) {
    el.removeEventListener('input', el._debounce)
    delete el._debounce
  }
})

new Vue({
  el: '#app',
  methods: {
    search(event) {
      console.log('搜索:', event.target.value)
    }
  }
})
</script>

binding 属性速查表

属性说明示例
name指令名'demo'
value绑定值2
oldValue旧值1
expression表达式'1 + 1'
arg参数'foo'
modifiers修饰符{ bar: true }

理解钩子参数后,接下来学习实用的指令示例。