元素命令点击外部源代码分析的详细说明
Clickoutside是由Element-ui实现的自定义指令。顾名思义,该指令用于处理目标节点外的click事件,通常用于关闭下拉菜单等扩展内容。这个指令用在Element-ui的Select选择器、下拉菜单、Popover弹出框等组件中,所以这个指令在实现一些用户自定义组件时非常有用。
要分析源代码,首先要了解Vue的自定义指令。自定义命令定义如下:
//注册全局自定义指令vue.directive ('directivename ',{bind:function (El,binding,vnode){//第一次将指令绑定到元素时调用,常用于一些初始化设置.}.当update:function (El,binding,vnode){//所在组件的VNode更新时,调用Inserted:function (El,binding,VNode,old VNode){//调用。但是它可能会在其子节点VNode更新之前发生.},组件更新了:函数(el,binding,vnode,old VNode){//组件的VNode及其子VNode全部更新后调用该命令.},Unbind :函数(El,binding,vnode){//只调用一次,在指令与元素解除绑定时调用,类似于beforeDestroy函数.}});您可以看到在配置对象中只有五个可选的钩子函数,它们有四个参数,即el、binding、vnode和oldVnode
El:指令绑定的元素可以用来直接操作DOM绑定:一个包含自定义细节的对象,内部收集使用自定义指令时传入的值、修饰符、参数等数据。细节可以在官方文件中看到。已经说得很详细了,Vnode 3360Vue编译生成的虚拟节点oldVnode:在这次Vnode更新之前生成的最后一个虚拟节点只在update和componentUpdated钩子中可用。在阅读了自定义指令的内容后,我们将分析clickoutside的具体实现。
从“Vue”导入Vue;从“element-ui/src/utils/dom”导入{ on };const nodeList=[];const CTX=' @ @ clickoutdecontext ';让startClick让seed=0;Vue.prototype.$isServer on(文档,‘mouse down’,e=(startClick=e));Vue.prototype.$isServer on(文档,“mouseup”,e={ nodeList.forEach(节点=node[ctx])。documentHandler(e,startClick));});函数createDocumentHandler(el,binding,vnode) {返回函数(mouseup={},mouse down={ }){ 0.};}让startClick让seed=0;导出默认值{ bind(el,binding,vnode)}.},更新(el,binding,vnode){ 0.},解除绑定(El){ 0.}};以上是简化的源代码。可以看到首先引入了Vue和一个用于事件绑定的工具函数on,然后定义了两个全局常量nodeList和ctx。NodeList是一个元素收集器,存储页面中所有与click out指令绑定的dom元素,而ctx定义了一个命名空间(必须是特殊的,以免和其他特性同名),后面会作为element el的属性加入,后面会分析。
然后,使用前面介绍的Vue来判断,非服务器端在文档对象中添加mousedown和mouseup事件,在mousedown事件回调中将事件对象存储在startClick全局变量中,在mouseup事件回调中遍历nodeList,然后执行存储在每个节点的CTX属性中的documentHandler函数(即之前存储的clickoutside指令绑定的元素El)。ctx属性的值将在后面介绍。
最后导出click out的一个配置对象,导入到使用click out指令的组件中,在组件中本地注册后即可使用。
在这个配置对象中,绑定、更新和解除绑定三个钩子函数用于定义clickoutside指令。要做的主要事情是收集这个自定义指令的相关信息,然后将其存储在el的ctx特性中。接下来,让我们具体看看这个收集过程。
第一个是绑定钩子函数:
bind(el,binding,vnode){ nodelist . push(El);const id=seedel[ctx]={ id,document handler : createDocumentHandler(El,binding,vnode),method name : binding . expression,binding n3 3360 binding . value };}这里先把el直接推入nodeList,这样每次clickoutside指令绑定到页面时,绑定的元素都会存储在nodeList中,也就是前面提到的元素收集器。接下来,全局变量seed被分配给临时变量id,最后,el的ctx属性被分配。它的值是一个对象,里面包括:
标识:上面生成的全局唯一标识用于标识点击外部指令
文档处理程序:由createDocumentHandler生成的回调函数。前面的分析中说,在绑定到页面的mouseup事件回调中,会遍历nodeList,并且会单独执行每个绑定元素el的ctx特性上的documentHandler函数,这个函数就是在这里生成的。至于回调函数做了什么,后面会详细分析。
MethodName :binding.expression,从自定义指令的文档中可以看出binding.expression的值是字符串形式的指令表达式。例如,如果有div v-my-directive='1 1'/div,binding.expression的值就是1 1
BindingFn :binding.value,指令的绑定值,就是上面的例子,那么binding.value的值就是2 (1 1等于2),也就是当指令的值是js表达式,* * binding的时候。表达式* *是表达式本身,它是一个字符串,而**binding.value**是这个表达式的值。
然后让我们看看更新挂钩:
更新(el,绑定,vnode) {el[ctx]。document handler=createDocumentHandler(El,binding,vnode);el[ctx]。methodName=binding.expressionel[ctx]。bindingFn=binding.value}您可以看到update hook的内容非常简单,即当组件更新时,绑定元素el的属性ctx中的值也会更新。
那么让我们来看看最后一个钩解除绑定:
unbind(El){ let len=nodelist . length;for(设I=0;我透镜;i ) { if (nodeList[i][ctx]。id===el[ctx]。id) { nodeList.splice(i,1);打破;} }删除El[CTX];}这个钩子也很简单,就是当clickoutside指令解绑定元素el时,遍历nodeList,在ctx属性上通过id找到nodeList中存储的当前解绑定元素el,从nodeList中删除,在el上删除ctx属性。
以上是clickoutside命令配置对象中完成的所有操作,可以总结如下:
当指令绑定到元素,组件被更新时,绑定元素的ctx特性被收集和设置,绑定元素被添加到nodeList。当指令与元素解除绑定时,存储在nodeList中的相应绑定元素被删除,并且先前在绑定元素上设置的ctx特征被删除。
如前所述,在页面绑定的mouseup事件回调中,将遍历nodeList,并且将分别执行ctx特性中的documentHandler函数,该函数位于每个收集的绑定元素el上。这个函数是由createDocumentHandler函数生成的,所以让我们看看这个函数做了什么。
函数createDocumentHandler(el,binding,vnode) {返回函数(mouseup={},mousedown={}) { if(!vnode ||!vnode.context ||!mouseup.target ||!mouse down . target | | El . contains(mouseup . target)| | El . contains(mousedown . target)| | El===mouseup . target | |(vnode . context . popper lem(vnode . context . popper lem . contains(mouseup . target)| | vnode . context . popper lem . contains(mousedown . target)))返回;if (binding.expression el[ctx]。method name vnode . context[El[CTX]。method NAmE]){ vnode . context[El[CTX]。method name]();} else { el[ctx]。bindingFn el[ctx]。binding fn();} };}可以看到,这个函数使用闭包来缓存传入的参数,然后返回一个函数。在这个返回的函数中,将进行一系列的判断。首先,在第一个if中,判断为:
vnode.context是否存在,existing mouseup.target是否存在,exiting mousedown.target是否存在,exiting binding object el是否包含mouse up . target/mouse down . target子节点,如果包含表示在绑定元素内部点击,那么未点击外部指令内容的绑定对象el是否等于mouse up . target表示点击绑定元素本身,不执行点击外部指令的内容。最后,vnode . context . popperem的内容为:判断是否在下拉菜单中点击。如果是,则不在绑定元素外点击,不执行点击外部指令的内容。
如图,如果点击在红色区域,click out指令的逻辑根本不会被触发。
如果满足以上所有条件,则判断闭包的缓存值,如果methodName存在,则执行此方法,如果不存在,则执行bindingFn。例如:
template div-click out=' handleClose '/div/template script导出默认值{ data(){ return { visible : false };},methods : { handleClose(){ this . visible=false;}} }/script在本例中,methodName或bindingFn是由指令传入的handleClose方法。通过执行此方法,您可以执行clickoutside指令的逻辑
以上是documentHandler方法的生成及其内部逻辑。通过这个方法和前面的分析,我们可以知道当触发页面绑定mouseup事件时,会遍历nodeList,依次执行每个绑定元素el的ctx特性上的documentHandler方法。该方法可以访问指令传入的表达式,经过一系列判断后执行表达式,从而达到点击目标元素外部执行给定逻辑的目的,通过定制指令的值,传递给绑定元素el的ctx特性。
至此,对clicking的源代码进行了分析,可以看出clicking指令的源代码并不复杂,但是涉及的内容还是很多的,有很多值得学习的地方,比如利用dom元素的特性来存储额外的信息,利用闭包来缓存变量,如何判断在目标元素和Vue自定义指令之外使用clicking。
版权声明:元素命令点击外部源代码分析的详细说明是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。