手机版

vue自定义指令的用法

时间:2021-08-26 来源:互联网 编辑:宝哥软件园 浏览:

Vue内置了很多指令,比如v-model、v-show、v-html等。但有时这些指令不能满足我们,或者我们想给元素增加一些特殊的功能。这时,我们需要在vue中使用一个强大的功能——自定义指令。

在开始之前,我们需要明确自定义指令解决的问题或者使用场景是操作底层的普通DOM元素,所以不能盲目使用自定义指令。

如何声明自定义指令?

就像vue中有全局组件和局部组件一样,也分为全局自定义指令和局部指令。

让Opt={ bind:function(el,binding,vnode) {},inserted:function(el,binding,vnode) {},update:function(el,binding,vnode){ },Component updated : function(El,binding,vnode){ },unbind : function (El,binding,vnode){ },对于全局自定义指令的创建,我们需要使用Vue指令接口。

对于本地组件,我们需要在组件的钩子函数指令中声明它们

指令: { demo : Opt } the中的指令可以缩写,上面的Opt是一个对象,包含五个钩子函数,我们可以根据需要只编写其中的几个。如果您想在绑定和更新时触发相同的行为,并且不关心其他钩子,您可以将Opt更改为一个函数。

让opt=function (el,binding,vnode) {}如何使用自定义指令?

自定义指令的使用非常简单,如果你对vue有一定的了解。

像v-text=“‘test’”一样,我们可以在“=”符号后传递需要传递的值。

我们可以通过参数“click”来获得像on: click=“握手”这样的指令。

我们可以像on:click那样给指令添加一个修饰符。stop=“握手”。

我们也不能像v-once那样传递任何东西。

每个指令的底层包肯定是不一样的,所以在使用之前要了解它的功能和用法。

自定义指令的钩子函数

正如我们在上面介绍的,定制指令中有五个钩子函数,分别是:绑定、插入、更新、组件更新和解除绑定。

指令定义对象可以提供以下挂钩函数(全部可选):

绑定:只调用一次,当指令第一次绑定到元素时调用。在这里,您可以进行一次性初始化设置。插入:当绑定元素插入到父节点时调用(只保证父节点存在,但不一定插入到文档中)。更新:当组件的VNode更新时会调用它,但它可能发生在其子VNode更新之前。指令的值可能已更改,也可能未更改。但是,您可以通过比较更新前后的值来忽略不必要的模板更新(有关钩子函数参数的详细信息,请参见下文)。ComponentUpdated:在指令所在组件的VNode及其子VNode全部更新后调用。Unbind:仅调用一次,当指令与元素解除绑定时调用。指令挂钩函数通过以下参数传递:

El:指令绑定的元素,可以用来直接操作DOM。绑定:具有以下属性的对象:名称:指令名称,不包括v前缀。Value:指令的绑定值,例如,在v-my-directive='1 1 '中,绑定值为2。OldValue:指令绑定的前一个值,只在update和componentUpdated钩子中可用。无论值是否改变都可用。表达式:字符串形式的指令表达式。例如,在v-my-direction=' 1 1 '中,表达式为' 11 '。Arg:传递给指令的参数,可选。例如,在v-my-directive:foo中,参数是“foo”。修改器:包含修改器的对象。例如,在v-my-direct.foo.bar中,修饰符对象为{foo: true,bar: true}。虚拟节点:由VUE编译生成的虚拟节点。有关更多详细信息,请访问VNode API。OldVnode:上一个虚拟节点,只在update和componentUpdated钩子中可用。对于这些钩子函数,你可以跳过你知道的,不知道的我就不介绍了。如果你自己去官网看,没有比官网说的更详细的了:钩子功能

项目中的错误

在项目中,我们自定义了一个全局指令my-click:

Vue.directive('my-click ',{ bind:function(el,binding,vnode,oldVnode){ El . addeventlistener(' click ',function(){ console.log(el),同时还有一个arr:[1,2,3,4,5,6]的数组。我们遍历数组来生成dom元素,并为这些元素绑定指令:

ul Li v-for=' arr ' : key=' index ' v-my-click=' item ' { item } }/Li/ul

我们可以看到,当我们点击元素时,元素被成功打印,过去的数据被传输。

然而,当我们动态地将最后一个元素更改为8 (6-8)时,单击该元素,元素是正确的,但打印的数据仍然是6。

或者,当我们删除第一个元素时,单击该元素

黑色问号脸,这是为什么?带着这个问题,我看了看源代码。在下面的源代码分析之前,我们先来说说结论:

组件初始化时,也就是第一次运行指令时,会执行bind hook函数,我们传入的所有参数(binding)都在这里输入,形成闭包。

我们在更新数据的时候,vue virtual dom不会破坏这个组件(如果删除了某个数据,组件会从后往前破坏,最后总是会破坏前一个),而是会更新(根据数据变化)。如果指令有update hook,这个hook函数会运行,但是如果在update中没有处理元素在bind中被绑定的事件,它就不会消失(仍然是指初始化时形成的闭包中的数据),所以当我们更改数据时,

源代码分析

函数的执行顺序:create elm/init component/patchvnode-invoke create hooks(CBS . create)-update directions-_ update

当createElm方法、initComponent方法和更新节点patchVnode时,将调用InvokeCreateHooks方法,它将遍历cbs.create中的hook函数来执行。cbs.create中有八个钩子函数,如下图所示。我们需要看到的是函数updateDirectives,它将继续调用_update函数,vue中的指令操作都在这个_update函数中。

让我们仔细看看这个_update函数。

Function _ update (oldvnode,vnode){//判断旧节点是否为空节点,如果是,则表示创建/初始化组件var为create=oldvnode===emptynode//判断新节点是否为空,如果是,则表示销毁组件var is destroy=vnode===empty node;//获取旧节点上的所有自定义指令varoldbits=normalize指令$ 1(old vnode . data . instructions,old vnode . context);//获取新节点上的所有自定义指令var newdirs=normalize指令$ 1(vnode . data . instructions,vnode . context);//保存插入的钩子函数var dirsWithInsert=[];//保存组件更新后的钩子函数var dirswithpost patch=[];var键,oldDir,dir//我们来说说callHook$1的功能。//callHook$1有五个参数,第一个参数是指令对象,第二个参数是Hook函数名,第三个参数是new node,//第四个参数是old node,第五个参数是是否是注销组件,默认不定义,只在组件注销时使用。//在这个函数中,根据我们传递的钩子,在运行我们的自定义组件时,声明的钩子函数//在所有新节点上遍历(newdirs中的key){ olddir=olddir[key]的自定义指令;dir=NewDiRS[key];//如果旧节点没有对应的指令,如果(!OldDir) {//在此节点上执行指令的bind hook函数callhook $1 (dir,‘bind’,vnode,old vnode);//dir.def是我们定义的指令的五个钩子函数的集合。//如果我们的指令中有插入的钩子函数If(dir . defdir . def . insert){//将指令保存到dirsWithInsert (dir)中;}} else {//如果旧节点中有对应的指令,通常在组件更新时运行。//然后运行更新挂钩(如果有)。//保存旧值以便在其他地方使用(仅在更新和组件更新挂钩中可用)。dir . old value=old dir . value;update hook函数callhook $1 (dir,' update ',vnode,old vnode)在此节点上执行指令;//dir.def是我们定义的指令的五个钩子函数的集合。//如果我们的指令中有componentUpdated hook函数If(dir . defdir . def.component updated){//将指令保存到dirswithpostpach(dir)中;} } }//先简单说一下mergeVNodeHook的功能。//mergeVNodeHook有三个参数,第一个参数是vnode节点,第二个参数是key value,第三个参数是return函数。//mergeVNodeHook将首先用函数wrappedHook重新打包回调。在此函数中运行回调函数。//如果节点没有这个键属性,则会增加一个新的键属性,其值为数组,数组中包含上述函数wrapperhook。//如果节点具有此键属性,函数wrapperhook将被追加到数组中。//如果dirsWithInsert的长度不为0,即初始化期间,并且至少有一条指令有插入的钩子函数if(dirsWithInsert.length) {//封装回调函数var callInsert=function() {//遍历所有指令的插入钩子(var I=0;i dirsWithInsert.lengthI) {//在节点上执行指令的插入钩子函数callhook $1 (dirswithinsert [I],' insert ',vnode,old vnode);} };如果(isCreate) {//如果正在创建/初始化组件,请使用mergeVNodeHook绑定insert属性并等待稍后的调用。mergeVNodeHook(vnode,' insert ',callInsert);} else {//如果是更新的组件,直接调用函数,遍历插入的钩子callInsert();} }//如果dirsWithPostpatch的长度不为0,即组件更新时,且至少有一条指令具有componentUpdated hook函数if (dirswithpostpatch。length){//使用mergeVNodeHook绑定postpatch属性,等到所有子组件都更新完成调用。

mergeVNodeHook(vnode,' postpatch ',function(){ for(var I=0;i dirsWithPostpatch.lengthI) {//componentUpdated hook函数call hook $ 1(dirswithpost patch[I],' componentUpdated ',vnode,old vnode)用于在节点上执行指令;} });}//如果组件不是新创建/初始化的,也就是说,如果(!IsCreate) {//遍历旧节点中的(旧计时器中的键)指令{//如果新节点中没有这样的指令(旧节点中有,但新节点中没有),如果(!NewDirs[key]) {//从旧节点解除绑定,isDestroy表示组件是否注销//解除绑定钩子函数callhook $1 (oldtags [key],' unbind ',oldvnode,oldvnode,IsDestroy)在旧节点上执行指令;}} }}callHook$1函数

函数callHook$1(dir,Hook,vnode,oldVnode,IsDestroy){ var fn=dir . def dir . def[hook];if(fn) {尝试{ fn(vnode.elm,dir,vnode,oldVnode,isdestory);} catch(e) { handleError(e,vnode.context,(' directive '(dir . name)' hook ' hook '));}}}求解

看完源代码,让我们回到上面的bug。应该如何解决?

1.解除绑定和重新绑定事件

我们在绑定钩子中绑定事件,当数据更新时,更新钩子将运行,因此我们可以解除绑定,然后在更新中再次绑定。由于bind和update的内容相似,我们可以将bind和update合并成同一个函数,用自定义指令的速记方法编写如下代码:

Vue.directive ('my-click ',函数(el,binding,vnode,old vnode){//click事件的回调挂在元素el.myclick的myClick属性上el.removeeventlistener ('click ',El . my click);el.addEventListener('click ',El . myclick=function(){ console . log(El,binding . value)})})

如你所见,数据已经成为我们想要的。

2.将绑定挂在元素上,并在更新数据后更新绑定

正如我们已经知道的,问题的根本原因是,当bind hook初始化时,它是一个元素绑定事件,事件中获得的数据是初始化期间传递的数据。因为形成了闭包,所以我们不使用可能导致闭包的数据,而是将数据保存在某个地方,然后更新数据。

Vue.directive('my-click ',{ bind: function(el,binding,vnode,oldVnode){ El . binding=binding El . addeventlistener(' click '),function () {var binding=this。bindingconsole.log (this,binding.value)}),更新:函数(El,binding,vnode,old vnode) {El。binding=binding}})也可以达到预期的效果。

3.更新父元素

如果我们将改变后的键值绑定到父元素ul,那么当数据发生变化时,父元素就会更新,从而重新创建子元素,达到重新绑定指令的效果。

Ul:键='日期。now ()' Li v-for=' (item,index)在arr' :键=' index ' v-my-click=' item ' { { item } }/Li/ul,这样也可以达到预期的效果。

以上就是本文的全部内容。希望对大家的学习有帮助,支持我们。

版权声明:vue自定义指令的用法是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。