概述
除了 Vue 原生的 v-model、v-show 等等指令外,Vue 也允許註冊 自定義指令(Custom Directives)。
Vue 不建議透過 document.querySelector 等方式直接選取 DOM 操作 DOM。但在開發過程中某些情況下還是有需要對底層 DOM 元素進行操作需求。
- 在 Vue 中,可以透過 ref來取得 DOM;
- 或者透過 自定義指令,封裝可重複使用的 DOM 操作功能。
簡單範例
當我們想要載入頁面時,讓某個表單元素自動獲取焦點,可以使用 autofocus 特性,但 iOS Safari 不支援。
這裡我們使用自定義指令來實現類似的效果。
|  1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
 | <script>
  const focus = {
    mounted(el) {
      el.focus();
    },
  };
  export default {
    directives: { focus },
  };
</script>
<template>
  <input v-focus />
</template>
 | 
 
註冊
自定義指令的註冊方式分成兩種:
鉤子函式
一個指令定義物件可以提供如下幾個鉤子函數(均為可選):
|  1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 | const myDirective = {
  created(el, binding, vnode) {
    // 在綁定元素的 attribute 或事件監聽器被應用之前呼叫(v-on 之前)
  },
  beforeMount(el, binding, vnode) {
    // 當指令第一次綁定到元素並且在掛載父元件之前呼叫
  },
  mounted(el, binding, vnode) {
    // 綁定元素的父元件被掛載時呼叫
  },
  beforeUpdate(el, binding, vnode, prevNode) {
    // 在包含元件的 VNode 更新之前呼叫
  },
  updated(el, binding, vnode, prevNode) {
    // 在包含元件的 VNode 及其子元件的 VNode 更新之後呼叫
  },
  beforeUnmount(el, binding, vnode) {
    // 在綁定元素的父元件卸載之前呼叫
  },
  unmounted(el, binding, vnode) {
    // 卸載綁定元素的父元件時呼叫
  },
};
 | 
 
鉤子函式參數
除了 el 之外,其餘參數都應該視為唯讀,請勿進行修改。若要在鉤子之間共享資料,建議透過元素的 dataset 來進行。
1. el
使用指令的元素。這可用於直接操作 DOM。
2. binding
binding 為物件,包含以下屬性:
- instance:使用指令的元件實體。
- value:傳遞給指令的值。
- oldValue:更新前的值,只在- beforeUpdate和- updated中可以使用。
- arg:參數。
- modifiers:包含修飾符號的物件。- 
- 例:v-demo.a會得到{ a: true }
 
- dir:指令物件。
3. vnode
虛擬節點。
4. prevNode
上一個虛擬節點,只在 beforeUpdate 和 updated 鉤子中可以使用。
函式簡寫
在很多時候,我們會在 mounted 和 updated 時觸發相同行為,而不關心其它的鉤子,Vue 為此提供了簡寫,傳遞一個函式:
| 1
2
3
 | const myDirective = (el, binding)=> {
  // ...
};
 | 
 
在元件上使用
- 指令也可以用在 Vue 的元件上,將會被應用在元件的根結點上;
- 但要注意,指令無法透過 v-bind="$attrs"被傳入到另一個元素;
- 若元件包含多個根結點,指令將會被忽略,並且拋出警告。
簡單應用
使用自定義指令簡易封裝 Bootstrap 5 的 Tooltips。
- value:內容 v-tooltip="'內容'"
- arg:方向 v-tooltip:right="'內容'"
|  1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
 | const { Tooltip } = bootstrap;
  
function tooltipInit(el, { value, arg }) {
  const tooltip = new Tooltip(el, {
    title: value,
    trigger: 'hover',
    placement: arg,
  });
  return tooltip;
}
const tooltip = {
  mounted(el, { value, arg = 'auto' }) {
    const tooltip = tooltipInit(el, { value, arg });
    el.$tooltip = tooltip;
  },
  updated(el, { value, arg = 'auto' }) {
    el.$tooltip.disable();
    const tooltip = tooltipInit(el, { value, arg });
    el.$tooltip = tooltip;
  },
  unmounted(el) {
    el.$tooltip.disable();
  },
};
 |