什么是自定义指令?
- 除了
Vue
内置的一系列指令之外,还允许注册自定义指令 - 主要为了重用涉及普通元素的底层
DOM
访问的逻辑 - 本质: 类似组件生命周期钩子的对象
局部注册
- 示例:
input
元素自动聚焦
<input v-focus />
export default {
directives: {
focus:{
inserted: (el) => el.focus()
}
}
};
- 页面加载时,
input
元素将获得焦点 - 注意:
autofocus
在移动版Safari
不工作
全局注册
- 全局共享的自定义指令需要通过
Vue.directive()
进行声明
/*
@/directives/index.js
全局注册指令
*/
const focus = {}
focus.install = (Vue)=>{
Vue.directive('focus',{
inserted(el){
el.focus()
}
})
}
export default focus
/*
main.js
挂载指令
*/
import focus from '@/directives'
Vue.use(focus)
<!-- App.vue 使用指令 -->
<input v-focus />
钩子函数
- 指令定义对象提供如下几个钩子函数
bind
- 只调用一次,指令第一次绑定到元素时调用
- 时机: 当指令绑定到元素上会立即执行一次
- 用途: 进行一次性的初始化设置
<h1 v-color >hello</h1>
<h2 v-color >javaScript</h2>
directives: {
color:{
bind(){
console.log('执行'); // 输出两次
}
}
}
inserted
- 指令所在元素被插入页面时调用(仅保证父节点存在,但不一定已被插入文档中)
<input v-focus />
directives: {
focus:{
inserted(el){
el.focus() // 此语句写在bind钩子中不生效
}
}
}
bind
和inserted
的区别
-
共同点:
DOM
插入时都会调用,bind
在inserted
之前 -
差异点:
- bind: 指令绑定立即调用( dom树绘制前 ),所在节点的父节点是
null
- inserted: 所在节点的父节点存在,节点已插入页面( dom树绘制后 )
- bind: 指令绑定立即调用( dom树绘制前 ),所在节点的父节点是
update
- 所在组件的
VNode
更新时调用,但是可能发生在其子VNode
更新之前 - 指令的值可能发生了改变,也可能没有
<h1 type="text" v-fbind >{{n}}</h1>
<button @click="n++">点击n+1</button>
export default {
data(){
return {n:1}
},
directives: {
fbind:{
update(){
console.log('点击了按钮');
}
}
}
};
componentUpdated
- 指令所在组件的
VNode
及其子VNode
全部更新后调用
<div type="text" v-fbind >
<span>{{n}}</span>
</div>
directives: {
fbind:{
update(el){
console.log('update:',el.innerHTML);
},
componentUpdated(el){
console.log('componentUpdated',el.innerHTML);
}
}
}
componentUpdated
和update
的区别
-
注意
componentUpdated
和update
钩子的区别:- update: 指令所在节点更新立马调用
- componentUpdated: 待指令所在节点的子节点也全部更新,才调用
- 相同点: 组件更新都会调用,
update
在componentUpdated
之前
unbind
- 只调用一次,指令与元素解绑时调用
<h1 v-if="btn" v-fbind>hello</h1>
<button @click="destory">关闭</button>
data(){
return {btn:true}
},
methods:{
destory(){
this.btn = false // 目的让自定义指令解绑
}
}
directives: {
fbind:{
unbind(){
console.log('unbind执行了');
}
}
}
钩子函数参数
- 钩子函数有四个内置的参数
<!-- 更新前 -->
<h1 id="content" v-demo:foo.a.b="message1" data-id="1">{{message1}}</h1>
<!-- 更新后 -->
<h1 id="content" v-demo:foo.a.b="message2" data-id="1">{{message2}}</h1>
data(){
return {
message1:'hello',
message2:'javaScript'
}
},
directives: {
demo:{
update(el, binding, vnode,oldVnode){
console.log('el:',el);
console.log('name:',binding.name);
console.log('value:',binding.value);
console.log('expression:',binding.expression);
console.log('arg:',binding.arg);
console.log('modifiers:',binding.modifiers);
console.log('modifiers:',binding.modifiers);
console.log('vnode:',vnode);
console.log('oldVnode:',oldVnode);
console.log('dataset:',el.dataset);
}
}
},
①el
: 指令所绑定的元素,可直接操作 DOM
②binding
: 一个对象,包含以下属性:
- name: 指令名,不带
v-
前缀 - value: 指令的绑定值
- oldValue: 指令绑定的前一个值, 仅在
update
和componentUpdated
钩子中可用 - expression: 字符串形式的指令表达式
- arg: 传给指令的参数,可选
- modifiers: 一个包含修饰符的对象
③vnode
: Vue
编译生成的虚拟节点,VNode API
④oldVnode
: 上一个虚拟节点,仅在 update
和 componentUpdated
钩子中可用
注意: 除了
el
之外,其它参数都是只读的,切勿进行修改钩子之间需共享数据,则通过元素的
dataset
来进行
动态指令参数
-
指令的参数可以是动态的
-
场景: 创建一个自定义指令,用于通过固定布局将元素固定在页面上
- ①固定在距离页面顶部 200 像素的位置
- ②固定在左侧
<p>Scroll down the page</p>
<p v-pin:[direction]="200">Stick me 200px from the top of the page</p>
data(){
return { direction:'left' }
},
directives: {
pin:{
bind(el, binding) {
el.style.position = 'fixed'
el.style.[ binding.arg === 'left'?'left' : 'top' ] = binding.value + 'px'
}
}
},
钩子函数简写
- 在
bind
和update
时触发相同行为,而不关心其它的钩子
<h1 v-pin v-focus>hello</h1>
- 局部写法
directives: {
pin() {
console.log('自定义指令生效了');
}
},
- 全局写法
const focus = {}
focus.install = (Vue)=>{
Vue.directive('focus',()=>{
console.log('focus指令生效了');
})
}
自定义指令的钩子里,this指向什么?
- 自定义指令的钩子里没有组件实例,
this
指向undefined
如何在自定义指令的钩子里面访问当前组件实例?
- 可以通过第三个参数
vnode
,vnode.context
就是指向当前组件实例,可以获取this
<h1 v-pin>hello</h1>
data(){
return { name:'app' }
},
directives: {
pin(el,binding,vnode) {
console.log(vnode.context);
const _this = vnode.context
console.log(_this.name);
}