除了 Vue 内置的一系列指令 (比如 v-model 或 v-show) 之外,Vue 還允許你注冊自定義的指令 (Custom
Directives)。
1.自定義指令的目的和簡單介紹
自定義指令主要是為了重用涉及普通元素的底層 DOM 通路的邏輯。
一個自定義指令由一個包含類似元件生命周期鈎子的對象來定義。鈎子函數會接收到指令所綁定元素作為其參數。
舉例:
在某個場景下,需要一進入頁面或者打開某個彈窗就聚焦到指定的輸入框。
局部的寫法:
<script setup>
const vFocus = {
created(el, binding,) {
},
beforeMount(el, binding,) {
},
mounted(el) {
el.focus()
},
beforeUpdate(a,b,c,prevNode) { //! 第四個參數 prevNode 隻在beforeUpdate和updated才有!
},
updated() {},
beforeUnmount() {
// 當指令綁定的元素 的父元件銷毀前調用。 <簡單講,指令元素的父元件銷毀前調用>
},
unmounted() {},// 當指令與元素解除綁定且父元件已銷毀時調用。
}
</script>
<template>
<input v-focus>
</template>
全局的寫法:
import { createApp } from 'vue'
const app = createApp(App)
app.directive('focus',{
created(el, binding,) {
},
beforeMount(el, binding,) {
},
mounted() {
el.focus()
},
beforeUpdate(a,b,c,prevNode) { //! 第四個參數 prevNode 隻在beforeUpdate和updated才有!
},
updated() {},
beforeUnmount() {
// 當指令綁定的元素 的父元件銷毀前調用。 <簡單講,指令元素的父元件銷毀前調用>
},
unmounted() {},// 當指令與元素解除綁定且父元件已銷毀時調用。
})
使用全局注冊需注意得寫在mount(‘#app’)之前,否則會報錯
我門可以發現對比vue2來看vue3裡自定義指令似乎在生命周期這塊發生了某些變化,下面我們來看看具體的。
注意:後面的示例都用局部的自定義指令來講解。
2.對比vue2自定義指令的生命周期
vue2自定義指令生命周期
- bind: function () {},
- inserted: function () {},
- update: function () {},
- componentUpdated: function () {},
- unbind: function () {}
vue3自定義元件生命周期
-
created:在綁定元素的 attribute 或事件監聽器被應用之前調用。在指令需要附加在普通的 v-on
事件監聽器調用前的事件監聽器中時,這很有用。
- beforeMount:當指令第一次綁定到元素并且在挂載父元件之前調用。
- mounted:在綁定元素的父元件被挂載後調用。
- beforeUpdate:在更新包含元件的 VNode 之前調用。
- updated:在包含元件的 VNode 及其子元件的 VNode 更新後調用。
- beforeUnmount:在解除安裝綁定元素的父元件之前調用
- unmounted:當指令與元素解除綁定且父元件已解除安裝時,隻調用一次。
這些生命周期都會接受以下幾個參數:
el, binding, vnode,
他們分别代表什麼意思呢?
el:指令綁定到的元素。這可以用于直接操作 DOM。
binding:一個對象,包含以下屬性。
- arg:傳遞給指令的參數 (如果有的話)。例如在 v-my-directive:foo 中,參數是 “foo”。
- dir:指令的定義對象。
- instance:使用該指令的元件執行個體。
- modifiers:一個包含修飾符的對象 (如果有的話)。例如在 v-my-directive.foo.bar 中,修飾符對象是 {foo: true, bar: true }。
- oldValue:之前的值,僅在 beforeUpdate 和 updated 中可用。無論值是否更改,它都可用。
- value:傳遞給指令的值。例如在 v-my-directive=“1 + 1” 中,值是 2。
vnode:代表綁定元素的底層 VNode。
prevNode:之前的渲染中代表指令所綁定元素的 VNode。僅在 beforeUpdate 和 updated 鈎子中可用。
這樣看可能抽象了點,我們在指令的mounted裡列印下這4個參數,可見:
<script setup>
const vFocus = {
created(el, binding,) {
},
beforeMount(el, binding,) {
},
mounted(el,binding, vnode,prevVnode) {
console.log('el--',el)
console.log('binding-',binding)
console.log('vnode-',vnode)
console.log('prevVnode-',prevVnode)
console.log()
el.focus()
},
beforeUpdate(a,b,c,prevNode) { //! 第四個參數 prevNode 隻在beforeUpdate和updated才有!
},
updated() {},
beforeUnmount() {
// 當指令綁定的元素 的父元件銷毀前調用。 <簡單講,指令元素的父元件銷毀前調用>
},
unmounted() {},// 當指令與元素解除綁定且父元件已銷毀時調用。
}
</script>
3.舉兩個栗子
我們主要看el和binding。
我們通過自定義指令來畫一個固定寬高和背景色的方塊吧!
//name:自定義參數,hqg:自定義修飾符
<div v-myDirective:name.hqg="{background: '#ccc', width: '200px', height: '200px'}">
這是一個div
</div>
const vMyDirective = {
mounted(el,binding, vnode,prevVnode) {
console.log('binding',binding)
el.style.background = binding.value.background
el.style.width = binding.value.width
el.style.height = binding.value.height
},
}