探索vue自定义指令的玄妙

Posted 恪愚

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了探索vue自定义指令的玄妙相关的知识,希望对你有一定的参考价值。

自定义指令的语法

Vue自定义指令语法如下:

Vue.directive(id, definition)

传入的两个参数,id是指指令ID,definition是指定义对象。其中,定义对象可以提供一些钩子函数:

钩子函数

定义对象的钩子函数如下:

  • bind:只调用一次,指令第一次绑定到元素时调用,用这个钩子函数可以定义一个在绑定时执行一次的初始化动作
  • inserted:被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于document中)
  • update:被绑定元素所在的模板更新时调用,而不论绑定值是否变化。通过比较更新前后的绑定值,可以忽略不必要的模板更新
  • componentUpdate:被绑定元素所在模板完成一次更新周期时调用
  • unbind:只调用一次,指令与元素解绑时使用

在钩子函数中,还有一些参数可供选择:

参数描述
el指令所绑定的元素,可以直接操作Dom
binding指令的描述对象
arg传给指令的参数,可选。例如 v-transfer:f40="{id:item.id,name:'mxc'}",在指令中可通过:binding.value.id/name 获取(这时 arg 就是 f40 )
vnodeVue 编译生成的虚拟节点
oldVnode上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用

而在binding中,还有几个属性:

  • name: 指令名,不包括v- 前缀
  • value: 指令的绑定值。例如: v-my-directive=”1 + 1”,value 的值是 2。
  • oldValue: 指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。
  • expression:字符串形式的指令表达式。例如 v-my-directive="1 + 1" 中,表达式为 “1 + 1”

简单使用

Vue自定义指令常见使用例子如下:

Vue.directive('my-directive', {
    bind: function(){
        //做绑定的准备工作
        //比如添加事件监听器,或是其他只需要执行一次的复杂操作
    },
    inserted: function(){
        //...
    },
    update: function(){
        //根据获得的新值执行对应的更新
        //对于初始值也会调用一次
    },
    componentUpdated: function(){
        //...
    },
    unbind: function(){
        //做清理操作
        //比如移除bind时绑定的事件监听器
    }
}

当指令的定义对象中只使用update时,只需直接传入函数即可,如下:

//全局方式
Vue.directive('my-directive', function(){
    //...
})
//局部的方式下面的例子就是

自定义指令是怎么和methods关联起来的

像简单的一些自定义指令比如赋值甚至内置的v-focus来说,使用非常方便,直接在回调中操作即可。
但是如果让你使用自定义指令实现拖拽、触底校验…这类复杂的操作呢?

这是完全有可能的。因为自定义指令的特殊性,我们完全可以用自定义指令做一些小的插件。而插件不可能涵盖所有的场景。

这时候就需要methods中函数(通过v-on,即@注册的)的参与了!
实际上,vue中是通过传参的形式,将methods中的函数传进来,以此来改变data中的值。就像这样:

methods:{
    set(x,y){
        this.data.x=x;
		this.data.y=y;
	}
},

directives:{
	// 拖动的自定义指令
	drag(el,binding){
		//el为拖动的元素
		var oDiv =el;
 		oDiv.onmousedown = function(e){
  			e.preventDefault();
     		e.stopPropagation();
  			var disX = e.offsetX;
  			var disY = e.offsetY;
  			document.onmousemove = function(e){
  				e.preventDefault();
				e.stopPropagation();
  				var x=e.pageX-disX;
  				var y=e.pageY-disY
   				oDiv.style.left=x
				oDiv.style.top=y
  				// 通过传参的形式,将methods中的函数传进来,以此来改变data中的值
  				binding.value.set(x,y)
  			};
  			document.onmouseup = function(){
  				document.onmousemove=null;
  				document.onmouseup=null;
  			};
 		};
	}
},

然后使用:

<div v-drag="{set:set}"></div>

指令的生命周期?

指令本质上是一个javascript对象,对象上挂载着一些钩子函数。
我们可以举个例子来说明:比如笔者定义一个v-log指令,这个指令做的事情就是在指令的各个生命周期中去输出一些log信息:

const logDirective = {
  	beforeMount() {
	    console.log('log directive before mount')
  	},
  	mounted() {
    	console.log('log directive mounted')
  	},
  	beforeUpdate() {
    	console.log('log directive before update')
  	},
  	updated() {
    	console.log('log directive updated')
  	},
  	beforeUnmount() {
    	console.log('log directive beforeUnmount')
  	},
  	unmounted() {
    	console.log('log directive unmounted')
  	}
}

然后你可以以全局指令 方式在创建应用后注册它:

import { createApp } from 'vue';
import App from './App';
const app=createApp(App);
app.directive('log',logDirective);
app.mount('#app');

使用上也很方便:

<template>
    <input v-if="flag" v-log v-model="text"/>
    <button @click="flag=!flag">toggle</button>
</template>
<script>
export default {
    data() {
        return {
      	    flag: true,
            text: ''
        }
    }
}
</script>

当你点击按钮后,会先执行指令定义的 beforeMountmounted 钩子函数,然后你在 input 输入框中输入一些内容,会执行 beforeUpdateupdated 钩子函数,然后你再次点击按钮,会执行 beforeUnmountunmounted 钩子函数。

所以一个指令的定义,无非就是在合适的钩子函数中编写一些相关的处理逻辑。

以上是关于探索vue自定义指令的玄妙的主要内容,如果未能解决你的问题,请参考以下文章

VSCode自定义代码片段——CSS选择器

VSCode自定义代码片段——.vue文件的模板

VSCode自定义代码片段6——CSS选择器

VSCode自定义代码片段——CSS动画

VSCode自定义代码片段(vue主模板)

VSCode自定义代码片段——声明函数