Try   HackMD
tags: Vue

【Vue】自訂義指令 (Custom Directives)

Vue 提供了各式各樣的指令,像是 v-modelv-show 等等,同時 Vue 也有提供讓你自定義指令的方法。

指令是讓用來操作普通元素的底層 DOM 元素的程式邏輯變的可覆用。

自定義指令是一個包含生命週期(類似元件的生命週期)的物件,他會關連到指令想操作的 DOM 元素。

<script setup> // enables v-focus in templates const vFocus = { mounted: (el) => el.focus() } </script> <template> <input v-focus /> </template>

example

這裡我們自定義了一個指令叫做 v-focus,這個指令會在 DOM 元素載入後時自動對指定的元素進行 focus。這種指令會比 html 原生的 autofocus 屬性來的方便,因為即便是 Vue 動態載入的元素也能作用。

使用

任何以駝峰式命名的變數只要是 v 開頭的話,寫在 <script setup> 中都可以被當作是自定義指令。

假設不是在 SFC 中,使用 directives 也能註冊指令

export default { setup() { /*...*/ }, directives: { // enables v-focus in template focus: { /* ... */ } } }

當然也能全域註冊

const app = createApp({}) // make v-focus usable in all components app.directive('focus', { /* ... */ })

關於自定義指令的使用時機: 如果你的程式有需要直接操作到 Dom 元素才推薦使用,其他情況比較推薦使用內建的 v-bind ,對於效能和 server-rending 比較友善。

指令掛勾(Directive Hooks)

一個指令物件可以包含多個 hook 函式

const myDirective = { // called before bound element's attributes // or event listeners are applied created(el, binding, vnode, prevVnode) { // see below for details on arguments }, // called right before the element is inserted into the DOM. beforeMount(el, binding, vnode, prevVnode) {}, // called when the bound element's parent component // and all its children are mounted. mounted(el, binding, vnode, prevVnode) {}, // called before the parent component is updated beforeUpdate(el, binding, vnode, prevVnode) {}, // called after the parent component and // all of its children have updated updated(el, binding, vnode, prevVnode) {}, // called before the parent component is unmounted beforeUnmount(el, binding, vnode, prevVnode) {}, // called when the parent component is unmounted unmounted(el, binding, vnode, prevVnode) {} }

Hook Arguments

  • el: 指令會操作的 Dom 元素
  • binding: 一個物件,包含以下屬性:
    • value: 傳給指令的 value (ex: v-my-directive="1 + 1", value: 2)
    • oldValue: 只會出現在 beforeUpdateupdated,代表 update 之前的 value
    • arg: 傳給指令的參數 (ex: v-my-directive:foo)
    • modifiers: 包含 modifier 的物件 (ex: v-my-directive.foo.bar,modiflier: { foo: true, bar: true })
    • instance: 指令操作的元件 instance
    • dir: 指令物件
  • vnode: 綁定普通元素的底層 vnode
  • prevNode: 只能在 beforeUpdateupdated 中使用,代表前一次渲染綁定的 vnode。

ex:

<div v-example:foo.bar="baz">

binding 物件:

{ arg: 'foo', modifiers: { bar: true }, value: /* value of `baz` */, oldValue: /* value of `baz` from previous update */ }

與內建的指令相同,自定義指令的參數也能是動態的

<div v-example:[arg]="value"></div>

為了與 el 做出區別,這些 argument 都必須是唯讀,你也不該更改他們,如果你希望在 hook 之間分享資訊,建議改使用 html 原本就有的 dataset 屬性

函式縮寫

自定義函式很長會在 mountedupdated 有相同行為,在其他 hook 沒有任何動作,所以 Vue 有提供一個縮寫方式:

app.directive('color', (el, binding) => { // this will be called for both `mounted` and `updated` el.style.color = binding.value })
<div v-color="color"></div>

物件實字 (Object Literal)

如果你的指令需要傳入多個值,你可以傳入物件實字

記住指令可以傳入任何的 javascript 表達式

<div v-demo="{ color: 'white', text: 'hello!' }"></div>
app.directive('demo', (el, binding) => { console.log(binding.value.color) // => "white" console.log(binding.value.text) // => "hello!" })

元件上的使用

在元件上使用時,也會像 Fallthrough Attritube 一樣,穿透到 root node。

<MyComponent v-demo="test" />

所以也和 Fallthrough Attritube 一樣,自定義指令會遇到多個 root node 的問題,如果遇到這種情況,Vue 會無視指令並留下警告,但不像 Fallthrough Attritube 一樣可以用 v-bind=$attrs來解決(其實也沒有解決方法),所以一般情況下不推薦直接在元件上使用自定義指令。

ex: https://blog.logrocket.com/deep-dive-custom-vue-directives/
https://codepen.io/_rahul/pen/mdBYREm?editors=1010