当前位置: 首页 > Web前端 > vue.js

v-clickoutside源码分析与改进

时间:2023-03-31 23:46:27 vue.js

1.前言最近遇到一个需求,公司需要做一个类似facebook的搜索选择组件,打算结合el-input和el-cascader-panel进行设计。设计过程中参考了el-cascader的源码,v-clickoutside自定义命令值得研究,所以写篇文章记录一下。2.基础知识点(1)v-directivevue文档已经写的很清楚了,这里只提供网址:vue中文文档-自定义指令(2)v-clickoutside先把源码放在element-ui//element-ui/src/utils/clickoutside.jsimportVuefrom'vue';import{on}from'element-ui/src/utils/dom';constnodeList=[];constctx='@@clickoutsideContext';letstartClick;让种子=0;!Vue.prototype.$isServer&&on(document,'mousedown',e=>(startClick=e));!Vue.prototype.$isServer&&on(document,'mouseup',e=>{nodeList.forEach(node=>node[ctx].documentHandler(e,startClick));});functioncreateDocumentHandler(el,binding,vnode){返回函数(mouseup={},mousedown={}){如果(!vnode||!vnode.context||!mouseup.target||!mousedown.target||el.contains(mouseup.target)||el.contains(mousedown.target)||el===mouseup。目标||(vnode.context.popperElm&&(vnode.context.popperElm.contains(mouseup.target)||vnode.context.popperElm.contains(mousedown.ta得到))))返回;if(binding.expression&&el[ctx].methodName&&vnode.context[el[ctx].methodName]){vnode.context[el[ctx].methodName]();}else{el[ctx].bindingFn&&el[ctx].bindingFn();}};}/***v-clickoutside*@desc点击元素外部才会触发的事件*@example*```vue**```*/export默认{绑定(el,绑定,vnode){nodeList.push(el);constid=seed++;el[ctx]={id,documentHandler:createDocumentHandler(el,binding,vnode),methodName:binding.expression,bindingFn:binding.value};},update(el,binding,vnode){el[ctx].documentHandler=createDocumentHandler(el,binding,vnode);el[ctx].methodName=binding.expression;el[ctx].bindingFn=binding.value;},unbind(el){letlen=nodeList.length;for(leti=0;i(clickInEvent=e))document.addEventListener('mouseup',e=>{nodeEventRecorder.forEach((value)=>{value(e,clickInEvent)})})functioncreateHandler(el,binding,vnode){返回函数(mouseup={},mousedown={}){if(!vnode||!vnode.context||!mouseup.target||el.contains(mouseup.target)||el.contains(mousedown.target)||el===mouseup.target||(vnode.context.popperElm&&(vnode.context.popperElm.contains(mouseup.target)||vnode.context.popperElm.contains(mousedown.target))){return}if(binding.expression&&vnode.context[binding.expression]){vnode.context[binding.expression]()复制代码}else{if(typeof(binding.value)==='function'){绑定ing.value()}else{thrownewError('valueshouldbeafunction')}}}}letdirective={bind(el,binding,vnode){nodeEventRecorder.set(el,createHandler(el,binding,vnode))},update(el,binding,vnode){nodeEventRecorder.set(el,createHandler(el,binding,vnode))},unbind(el){nodeEventRecorder.delete(el)}}exportdefaultdirective有人可能会问为什么不使用Object作为存储map的容器类型,因为Object的key只能是string或者Symbol类型的数据,而Map的key可以是任何数据类型,包括节点。替换为Map后,el[ctx]、seed++等变量不存在了。unbind中执行删除操作时不需要遍历链表。实现的效果如下: