請啟用 Javascript 查看內容

Vue3 - 自定義指令

 ·  ☕ 3 分鐘

概述

除了 Vue 原生的 v-modelv-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>

註冊

自定義指令的註冊方式分成兩種:

  • 如果有共用的需求,使用 app.directive() 進行全域註冊;
  • 只想要用在單一元件,則使用 directives 選項區域註冊。

鉤子函式

一個指令定義物件可以提供如下幾個鉤子函數(均為可選):

 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:傳遞給指令的值。
    • 例:v-demo="1+1" 會得到 2
  • oldValue:更新前的值,只在 beforeUpdateupdated 中可以使用。
  • arg:參數。
    • 例:v-demo:foo 會得到 "foo"
  • modifiers:包含修飾符號的物件。
    • 例:v-demo.a 會得到 { a: true }
  • dir:指令物件。

3. vnode

虛擬節點。

4. prevNode

上一個虛擬節點,只在 beforeUpdateupdated 鉤子中可以使用。

函式簡寫

在很多時候,我們會在 mountedupdated 時觸發相同行為,而不關心其它的鉤子,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();
  },
};

竹白
作者
竹白
前端筆記

文章目錄