问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

vue中如何自定义指令及其原理详解

发布网友 发布时间:2024-09-08 11:07

我来回答

1个回答

热心网友 时间:2024-10-30 16:00

如何自定义指令?

其实关于这个问题官方文档上已经有了很好的示例的,我们先来温故一下。

除了核心功能默认内置的指令(v-model和v-show),Vue也允许注册自定义指令。注意,在Vue2.0中,代码复用和抽象的主要形式是组件。然而,有的情况下,你仍然需要对普通DOM元素进行底层操作,这时候就会用到自定义指令。举个聚焦输入框的例子,如下:

当页面加载时,该元素将获得焦点(注意:autofocus在移动版Safari上不工作)。事实上,只要你在打开这个页面后还没点击过任何内容,这个输入框就应当还是处于聚焦状态。现在让我们用指令来实现这个功能:

//注册一个全局自定义指令`v-focus`Vue.directive('focus',{//当被绑定的元素插入到DOM中时……inserted:function(el){//聚焦元素el.focus()}})

如果想注册局部指令,组件中也接受一个directives的选项:

directives:{focus:{//指令的定义inserted:function(el){el.focus()}}}

然后你可以在模板中任何元素上使用新的v-focusproperty,如下:

<inputv-focus>指令内部提供的钩子函数

一个指令定义对象可以提供如下几个钩子函数(均为可选):

bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。

inserted:被绑定元素插入父节点时调用(仅保证父节点存在,但不一定已被插入文档中)。

update:所在组件的VNode更新时调用,**但是可能发生在其子VNode更新之前。**指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新(详细的钩子函数参数见下)。

componentUpdated:指令所在组件的VNode及其子VNode全部更新后调用。

unbind:只调用一次,指令与元素解绑时调用。

钩子函数参数

指令钩子函数会被传入以下参数:

el:指令所绑定的元素,可以用来直接操作DOM。

binding:一个对象,包含以下property:

name:指令名,不包括v-前缀。

value:指令的绑定值,例如:v-my-directive="1+1"中,绑定值为2。

oldValue:指令绑定的前一个值,仅在update和componentUpdated钩子中可用。无论值是否改变都可用。

expression:字符串形式的指令表达式。例如v-my-directive="1+1"中,表达式为"1+1"。

arg:传给指令的参数,可选。例如v-my-directive:foo中,参数为"foo"。

modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar中,修饰符对象为{foo:true,bar:true}。

vnode:Vue编译生成的虚拟节点。可以参考官网的VNodeAPI来了解更多详情。

oldVnode:上一个虚拟节点,仅在update和componentUpdated钩子中可用。

除了el之外,其它参数都应该是只读的,切勿进行修改。如果需要在钩子之间共享数据,建议通过元素的dataset来进行。

我们来看一个demo,

<divid="hook-arguments-example"v-demo:foo.a.b="message"></div>Vue.directive('demo',{bind:function(el,binding,vnode){vars=JSON.stringifyel.innerHTML='name:'+s(binding.name)+'<br>'+'value:'+s(binding.value)+'<br>'+'expression:'+s(binding.expression)+'<br>'+'argument:'+s(binding.arg)+'<br>'+'modifiers:'+s(binding.modifiers)+'<br>'+'vnodekeys:'+Object.keys(vnode).join(',')}})newVue({el:'#hook-arguments-example',data:{message:'hello!'}})

来看一下渲染的结果:

name:"demo"value:"hello!"expression:"message"argument:"foo"modifiers:{"a":true,"b":true}vnodekeys:tag,data,children,text,elm,ns,context,fnContext,fnOptions,fnScopeId,key,componentOptions,componentInstance,parent,raw,isStatic,isRootInsert,isComment,isCloned,isOnce,asyncFactory,asyncMeta,isAsyncPlaceholder指令实现原理解析

通过上面官网的例子和我们平时的coding,我们基本上了解了vue的指令是如何使用的了,接下来我们从源码的视角来解析其实现的原理。

Vue.directive的定义:functioninitAssetRegisters(Vue){ASSET_TYPES.forEach(function(type){Vue[type]=function(id,definition){if(!definition){returnthis.options[type+'s'][id]}else{if(type==='component'){validateComponentName(id);}if(type==='component'&&isPlainObject(definition)){definition.name=definition.name||id;definition=this.options._base.extend(definition);}if(type==='directive'&&typeofdefinition==='function'){//Tip:兼容传参definition={bind:definition,update:definition};}//Tip:储存一个['component','directive','filter']this.options[type+'s'][id]=definition;returndefinition}};});}

其实这个方法比较简单,就是在vm.options.directives挂载了一个映射,比如vm.$options.directives.demo={xxx},我们要看看这个指令是如何生效的。

在没有下一步对源码进行分析之前,我们也能大概猜测出自定义指令是如何实现的。在模板编译阶段,从元素的属性中解析到指令属性,在不同生命周期元素阶段调用自定指令中不同的自定义逻辑。接下来配合源码来分析一下,将这个指令解析和生效分为三个阶段:模板编译阶段,生成VNode阶段,以及生成真实Dom的patch阶段。

我们以下面的代码片段为例:

<divid="hook-arguments-example"v-demo:foo.a.b="message"></div>模板编译阶段

对模板编译不熟悉的同学可以去回顾一下,这个阶段大致做了什么。这里不去详细介绍了,只关注指令这一部分。指令是元素的属性的一部分,所以在解析标签元素时,会被放入EleAst这个对象的attrs属性中。上述的示例,会被解析为这样:

[{name:"id",value:"hook-arguments-example",start:5,end:32},{name:"v-demo:foo.a.b",value:"message",start:33,end:57}]

在匹配到结束标签时,会进一步处理这些属性,比如:如果是指令的话,会被处理为directives挂载到这个EleAst对象上。

具体的流程如下,在endTagMatch匹配到结束标签的时候,会去调用处理结束标签的parseEndTag函数,在这个函数内部回去调用parseHtml的配置项的options.end,其中又回去调用closeElement。

functioncloseElement(element){//...if(!inVPre&&!element.processed){element=processElement(element,options);}//...}

注意这里的processElement方法,主要是对解析过程中的元素进行各种加工。我们来看一下processElement的代码。

directives:{focus:{//指令的定义inserted:function(el){el.focus()}}}0

主要针对一堆元素属性的处理方法,我们需要关注processAttrs方法,它是处理指令和修饰符相关的方法。我们我看一下processAttrs的伪代码:

directives:{focus:{//指令的定义inserted:function(el){el.focus()}}}1

这里有个for循环去EleAst的attrsList,然后按照不同的正则去解析他们,分别处理v-bind,v-on以及v-xx的情形。对于自定义的指令会通过addDirective给EleAst添加directives属性,如下:

directives:{focus:{//指令的定义inserted:function(el){el.focus()}}}2

在模板解析的第一段阶段指令解析为上述模样。在模板解析的第二阶段generate将解析得到的EleAst生成产生vNode的函数字符串。自定义指令也转化为下面的形式了,成为_c函数的第二个参数了。

directives:{focus:{//指令的定义inserted:function(el){el.focus()}}}3生成vNode阶段

在这个render函数生成vNode的阶段,生面的指令字符串会被挂载到vNode.data.directives属性下,

directives:{focus:{//指令的定义inserted:function(el){el.focus()}}}4生成真实Dom的patch阶段

在这个由vNode生成真实Dom的阶段,createElm会去调用invokeCreateHooks(调用crate阶段所需要的函数),会去调用updateDirectives函数,这里面最终会去调用_update我们来看下代码:

directives:{focus:{//指令的定义inserted:function(el){el.focus()}}}5

在_update中,normalizeDirectives$1很重要,是它将我们一开始全局自定义的指令函数对应到当前的节点上。此外,在不同的生命周期也会依据不同的条件去调用不同自定义指令函数。比如,不存在oldDir,就会去调用初始化的bind。

总结

没有想象中的那么神秘,从一开始的Vue.directive全局函数的定义以及文档中给不同钩子函数的定义和灌入的参数,我们就有了大概的思路了。通过对自定义指令实现的一步步探究,对整个vue的流程有了更进一步的了解。此外让我印象深刻的是整个代码逻辑的组织,值得我们去进去挖掘和学习。

作者:Jayconscious

声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
诺基亚5320信号问题 诺基亚5320手机信号满别人打不进来电话是怎么回事? 诺基亚5320接电话每次自动信号断掉为什么? 诺基亚5320手机为什么有时候接听按键接不了电话,而平常玩手机接听键没... 诺基亚5320接不到电话 但是在通话记录里有 5320接不到电话 二手房多久能贷款下来 二手房贷多久能够放款 二手房贷审核通过多久放款 二手房贷款审核后多久能放款? 二手房银行放贷后多久 挪用公款罪的量刑数额及标准是怎样的 挪用资金罪数额较大和巨大标准是怎样的 凉粉果胶成分求解! 区块链中全网是什么(区块链全称是什么) 在中国买胡ipad平板电脑在土耳其能用吗? 电脑中出现了土耳其语言,怎么恢复成中文 土耳其的电脑为什么设置不了中文 写好作文的实用方法是什么? 天麻炖鸽子可以治头晕吗鸽子炖天麻一天吃多少 苹果手机信号为什么不好了? qq建群怎么建立一个新群聊 莲藕梨韭菜牛奶蜂蜜一块吃有什么功效 校花的贴身高手电视剧剧情 电视剧校花的贴身高手简介 藕粉对胃的好处 直观理解刚体运动中的欧拉方程(欧拉旋转公式-旋转运动中的牛二定律... 甲状腺癌用碘131治疗有副作用吗 主治医师晋升条件 医生职称怎么划分,如何一步步晋升职称? 华为HMS Core是什么,有用吗? 四平市的社保要怎么办理? 相关社保、医保的规定文件要怎么查,社保局的... vue插槽和作用域插槽(vue中插槽的作用) 笔记本电脑连不上wifi但手机可以? 桃和西瓜不能一起吃还有什么不能一起吃 一般侵占罪司法解释 成绩和成果是一个意思吗?? 个人侵占罪立案标准怎么规定 一个人实现自己的价值,成就自己和成就他人与服务社会之间是什么关系... 长江黄河流经省份图(是图来的哦) SDK是什么?有什么作用? 区块链sdk是什么(SDK有哪些) API与SDK之间有哪些区别?一文带你了解API和SDK! 泰山 诗句形容 100万左右预算,兰博基尼的这几款车型值得入手 兰博基尼URUS哪款颜色更能显示出URUS的霸气? 兰博基尼推出去回弹会在哪里出问题 京东会员怎么退掉 德美瓷砖质量如何 京东开了plus会员能退吗?怎么退? 京东免费开通会员后会自动续费吗?怎么取消? 京东品牌会员怎么退?品牌会员有什么好处?