浅谈Vue2选项--directives

404 阅读3分钟

什么是自定义指令?

  • 除了 Vue 内置的一系列指令之外,还允许注册自定义指令
  • 主要为了重用涉及普通元素的底层 DOM 访问的逻辑
  • 本质: 类似组件生命周期钩子对象

局部注册

  • 示例: input 元素自动聚焦
 <input v-focus />
 export default {
   directives: {
     focus:{
       inserted: (el) => el.focus()
     }
   }
 };
  • 页面加载时,input 元素将获得焦点
  • 注意: autofocus 在移动版 Safari 不工作

1666371977560.png

全局注册

  • 全局共享的自定义指令需要通过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('执行'); // 输出两次
     }
   }
 }

1666373850532.png

inserted

  • 指令所在元素被插入页面时调用(仅保证父节点存在,但不一定已被插入文档中)
 <input v-focus />
 directives: {
   focus:{
     inserted(el){
       el.focus() // 此语句写在bind钩子中不生效
     }
   }
 }

bindinserted 的区别

  • 共同点: DOM 插入时都会调用,bindinserted 之前

  • 差异点:

    • bind: 指令绑定立即调用( dom树绘制前 ),所在节点的父节点是 null
    • inserted: 所在节点的父节点存在,节点已插入页面( 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('点击了按钮');
       }
     }
   }
 };

1666373850532.png

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);
     }
   }
 }

1666374767210.png

componentUpdatedupdate 的区别

  • 注意 componentUpdatedupdate 钩子的区别:

    • update: 指令所在节点更新立马调用
    • componentUpdated: 待指令所在节点的子节点也全部更新,才调用
  • 相同点: 组件更新都会调用,updatecomponentUpdated 之前

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执行了');
     }
   }
 }

1666375504064.png

钩子函数参数

  • 钩子函数有四个内置的参数
 <!-- 更新前 -->
 <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

1666422514004.png


binding 一个对象,包含以下属性:

  1. name: 指令,不带 v- 前缀
  2. value: 指令的绑定值
  3. oldValue: 指令绑定的前一个值, 仅在 updatecomponentUpdated 钩子中可用
  4. expression: 字符串形式的指令表达式
  5. arg: 传给指令的参数,可选
  6. modifiers: 一个包含修饰符的对象

1666422538478.png


vnode Vue 编译生成的虚拟节点VNode API

1666422733930.png


oldVnode 上一个虚拟节点,仅在 updatecomponentUpdated 钩子中可用

1666422753744.png

注意: 除了 el 之外,其它参数都是只读的,切勿进行修改

钩子之间需共享数据,则通过元素的 dataset 来进行

1666422770336.png

动态指令参数

  • 指令的参数可以是动态的

  • 场景: 创建一个自定义指令,用于通过固定布局将元素固定在页面上

    • ①固定在距离页面顶部 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'
     }
   }
 },

1666423473475.png

钩子函数简写

  • bindupdate 时触发相同行为,而不关心其它的钩子
 <h1 v-pin v-focus>hello</h1>
  • 局部写法
 directives: {
   pin() {
     console.log('自定义指令生效了');
   }
 },
  • 全局写法
 const focus = {}
 ​
 focus.install = (Vue)=>{
  Vue.directive('focus',()=>{
   console.log('focus指令生效了');
  })
 }

1666423904830.png

自定义指令的钩子里,this指向什么?

  • 自定义指令的钩子里没有组件实例,this 指向 undefined

如何在自定义指令的钩子里面访问当前组件实例?

  • 可以通过第三个参数 vnodevnode.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);
 }

1666462242902.png