指令
vue 内置了一些指令,也提供了自定义指令的接口。
指令的作用:可把一些可复用的逻辑封装成指令,以实现逻辑复用。
指令依照应用范畴看,分为全局指令和部分指令(在某个组件外部应用的)。
指令和组件一样,具备一些在特定期间执行的函数,就是通过它们定义指令的。
应用形式
有一个v-test
指令。
<template> <div v-test:disabled.foo="'directive'"> <h1>{{ msg }}</h1> </div></template>
:
之后的是指令参数,相似v-on:keyup
中的 keyup
。.
之后的时指令修饰符,foo 是修饰符。
参数只能有一个,修饰符可有多个。
v-test:disabled:boo.foo.zoo="msg"
{ arg: "disabled:boo" modifiers: {foo: true, zoo: true}}
心愿不同状况下绑定不同的参数,可应用动静参数。
v-test:[argu].foo.zoo="msg"
参数必须在修饰符之前。
指令的等号前面是指令表达式,其值对应binding
对象的 value 属性。
binding
一个对象:
{ arg: "disabled", expression: "msg", modifiers: {foo: true}, name: "test", value: "你好",}
vue2 指令定义形式
以插件的模式定义一个全局v-click-outside
:
export const clickOutside = (Vue, options) => { Vue.directive('clickOutside', { inserted(el, binding, vnode) { const { value } = binding // const _this = vnode.context // NOTE 技巧:处理函数挂载在元素上,不便解绑时移出事件监听 el.onClick = ({ target }) => { if (el.contains(target)) { // 点击外部 console.log('clickInside') } else { // 点击内部 console.log('clickOutside') value && value() } } document.addEventListener('click', el.onClick, false) }, unbind(el, binding, vnode) { document.removeEventListener('click', el.onClick, false) }, })}
注册插件:
main.js
Vue.use(clickOutside)
应用:
<template> <div> <h3>测试 v-click-outside</h3> <div v-click-outside="onClickOutside"> <p>测试点击内部</p> <p>测试点击内部</p> <p>测试点击内部</p> <p>测试点击内部</p> </div> </div></template><script> import { Button } from 'element-ui' export default { name: 'WebComponent', components: { Button, }, data() { return { msg: 'Hello web components in stencil!', } }, methods: { onClickOutside() { console.log('onClickOutside') console.log(this.msg) }, }, }</script>
点击到div
内部时,就会执行onClickOutside
。
Vue.directive('directive-name', options)
options 是一个对象,蕴含一些生命周期钩子,这些生命周期都是可选的。
依照执行程序可:
bind # 只调用一次,指令绑定到元素时调用,父元素可能不存在。做初始化工作⬇️inserted # 只调用一次,被绑定的元素插入到父节点,父节点存在,此时可能被绑定元素还每插入文档中。⬇️update # 此时组件还没更新结束,拿不到最新的数据⬇️componentUpdated # 此时组件曾经更新结束 能拿到最新⬇️unbind # 只调用一次,指令和元素解绑,可做一些收尾工作
简写形式:
Vue.directive('color-swatch', function (el, binding) { el.style.backgroundColor = binding.value})
在 bind 和 update 时触发雷同行为,而不关怀其它的钩子。
钩子的参数
bind(el, binding, vnode)inserted(el, binding, vnode)update(el, binding, vnode, oldVnode) // update componentUpdated 还有额定的 oldVnodecomponentUpdated(el, binding, vnode, oldVnode)unbind(el, binding, vnode)
指令钩子函数的参数,次要关注el
和binding
。
el
是绑定指令的元素,可对其进行 DOM 操作。
binding
一个对象:
{ arg: "disabled", expression: "msg", modifiers: {foo: true}, name: "test", value: "你好",}
update
和 componentUpdated
钩子函数,binding 对象还有 oldArg
和 oldValue
属性。
指令钩子和组件生命周期的执行程序是怎么的?
挂载组件
beforeCreate⬇️created⬇️beforeMount⬇️bind # 指令 绑定到元素时调用,父元素可能不存在⬇️inserted # 指令 被绑定元素插入父元素时调用,父元素肯定存在⬇️mounted
更新组件
beforeUpdate⬇️update # 指令,此时组件还没更新结束,拿不到最新的数据⬇️componentUpdated # 指令 此时组件曾经更新结束 能拿到最新的数据⬇️updated
销毁组件
beforeDestroy⬇️unbind # 指令 组件在销毁之前调用,依然能拿到组件的数据⬇️destroyed
重建组件时
beforeCreate⬇️created (重建)⬇️beforeMount⬇️bind # 留神这里,指令绑定这个钩子函数,将会拿不到重建后的最新数据⬇️beforeDestroy (组件销毁)⬇️unbind # 指令⬇️destroyed⬇️inserted # 指令 应用该钩子函数,能拿到重建后的数据⬇️mounted
论断:只有
inserted
和componentUpdated
生命周期钩子,在执行时组件的 DOM 曾经更新结束,可放心使用。它们可获取到组件更新后的数据,指令绑定的元素的父元素也曾经存在。如何在指令生命周期中应用
this
或者拜访组件实例?
间接应用this
为 undefined,可应用vnode.context
获取:
inserted(el, binding, vnode) { // setTile 是组件 methods 里的办法 vnode.context.setTile(el)},componentUpdated(el, binding, vnode, oldVnode) { vnode.context.setTile(el)},
vue3 中的 指令
vue3 的指令钩子、钩子的参数和定义形式有变动。
定义形式
全局指令
app.directive('directive-name', directiveOptions)
简写模式:
app.directive('directive-name', (el, binding) => {}) // mounted 和 updated 操作雷同
部分指令
script setup 应用v
结尾命名。
<script setup> // 注册一个部分的自定义指令,须要以小写v结尾 const vFocus = { mounted(el, binding, vNode) { console.log(el) console.log(binding) console.log(vNode) // 获取input,并调用其focus()办法 el.focus() }, }</script><template> <input v-focus /></template>
setup 函数形式:
<script> export default { directives: { focus: { mounted(el, binding, vNode) { // 获取input,并调用其focus()办法 el.focus() }, }, }, setup() {}, }</script>
指令钩子的变动:
const myDirective = { // 在绑定元素的 attribute 前 // 或事件监听器利用前调用 created(el, binding, vnode) {}, // 在元素被插入到 DOM 前调用,可执行一些初始化工作 beforeMount(el, binding, vnode) {}, // 在绑定元素的父组件 // 及他本人的所有子节点都挂载实现后调用 mounted(el, binding, vnode) {}, // 绑定元素的父组件更新前调用 beforeUpdate(el, binding, vnode, prevVnode) {}, // 在绑定元素的父组件 // 及他本人的所有子节点都更新后调用 updated(el, binding, vnode, prevVnode) {}, // 绑定元素的父组件卸载前调用,收尾工作 beforeUnmount(el, binding, vnode) {}, // 绑定元素的父组件卸载后调用 unmounted(el, binding, vnode) {},}
罕用的钩子:
beforeMount # 相似 vue2 bindmounted # 相似 vue2 insertedupdated # 相似 vue2 componentUpdatedbeforeUnmount # 相似 vue2 unbind
binding 的参数有变动:减少了instance
属性用于获取组件实例。
创立一个检测点击 div 内部的指令:
import type { App } from 'vue'export const clickOutside = (app: App, options: any) => { app.directive('clickOutside', { mounted(el, binding) { const { value, instance } = binding // NOTE 技巧:处理函数挂载在元素上,不便解绑时移出事件监听 el.onClick = (event: Event) => { if (el.contains(event.target)) { console.log('clickInside') } else { // 点击内部 console.log('clickOutside') // console.log(instance)// 组件实例 value && value() } } document.addEventListener('click', el.onClick, false) }, beforeUnmount(el) { console.log('beforeUnmount') document.removeEventListener('click', el.onClick, false) }, }) return app}
雷同的性能应用 hook 实现:
import { onMounted, onBeforeUnmount, ref } from 'vue'export function useOnClickOutside(DOM = null, callback) { const isClickOutside = ref(false) function handleClick(event) { if (DOM.value && !DOM.value.contains(event.target)) { callback() isClickOutside.value = true } } onMounted(() => { document.addEventListener('mousedown', handleClick) }) onBeforeUnmount(() => { document.removeEventListener('mousedown', handleClick) }) return isClickOutside}
hook vs 指令
应用形式:hook 在 script 里,指令绑定到模板
定义形式:hook 更加简略,只需记住组件的生命钩子,复用更加不便,指令稍微繁琐,须要额定记忆指令钩子。
hook 能返回响应是对象,指令做不到。
hook 劣势更大,更加灵便
指令钩子函数和组件生命周期的执行程序
挂载阶段:
setuponBeforeMountcreated # 指令钩子beforeMount # 指令钩子mounted # 指令钩子onMounted
销毁阶段:
onBeforeUnmountbeforeUnmount # 指令钩子unmounted # 指令钩子onUnmounted
比拟好实际:只定义 mounted、updated 和 beforeUnmount 钩子。
其余问题
如何应用代码测试指令呢?
我搜寻不到相干教程。
github 的一个 issue
有请打大佬解答。
总结
指令是复用逻辑的无效形式,尤其是遇到须要间接操作 DOM 的状况。
vue2 中罕用的指令钩子:inserted、componentUpdated 和 unbind。
vue3 中罕用的指令钩子:mounted、updated 和 beforeUnmount。
vue3 中 script setup
定义部分指令应用 v
结尾。
vue3 的 binding 对象增加了instance
属性。
雷同的性能 hook 和指令都能实现,hook 劣势更大。