UNPKG

magix

Version:

view manager framewrok

286 lines (277 loc) 11.2 kB
/* dom event处理思路 性能和低资源占用高于一切,在不特别影响编程体验的情况下,向性能和资源妥协 1.所有事件代理到body上 2.优先使用原生冒泡事件,使用mouseover+Magix.inside代替mouseenter 'over<mouseover>':function(e){ if(!Magix.inside(e.relatedTarget,e.eventTarget)){ //enter } } 3.事件支持嵌套,向上冒泡 4.如果同一节点上同时绑定了mx-event和选择器事件,如 <div data-menu="true" mx-click="clickMenu()"></div> 'clickMenu<click>'(e){ console.log('direct',e); }, '$div[data-menu="true"]<click>'(e){ console.log('selector',e); } 那么先派发选择器绑定的事件再派发mx-event绑定的事件 5.在当前view根节点上绑定事件,目前只能使用选择器绑定,如 '$<click>'(e){ console.log('view root click',e); } range:{ app:{ 20:{ mouseover:1, mousemove:1 } } } view:{ linkage:{ 40:1 } } */ let Body_EvtInfoCache = new G_Cache(30, 10); let Body_EvtInfoReg = /(?:([\w\-]+)\x1e)?([^(]+)\(([\s\S]*)?\)/; let Body_RootEvents = {}; let Body_SearchSelectorEvents = {}; let Body_RangeEvents = {}; let Body_RangeVframes = {}; let Body_Guid = 0; let Body_FindVframeInfo = (current, eventType) => { let vf, tempId, selectorObject, eventSelector, eventInfos = [], begin = current, info = G_GetAttribute(current,`mx-${eventType}`), match, view, vfs = [], selectorVfId = G_HashKey, backtrace = 0; if (info) { match = Body_EvtInfoCache.get(info); if (!match) { match = info.match(Body_EvtInfoReg) || G_EMPTY_ARRAY; match = { v: match[1], n: match[2], i: match[3] }; Body_EvtInfoCache.set(info, match); } match = { ...match, r: info }; } //如果有匹配但没有处理的vframe或者事件在要搜索的选择器事件里 if ((match && !match.v) || Body_SearchSelectorEvents[eventType]) { if ((selectorObject = Body_RangeVframes[tempId = begin['@{node#owner.vframe}']]) && selectorObject[begin['@{node#guid}']] == 1) { view = 1; selectorVfId = tempId;//如果节点有缓存,则使用缓存 } if (!view) { //先找最近的vframe vfs.push(begin); while (begin != G_DOCBODY && (begin = begin.parentNode)) { //找最近的vframe,且节点上没有mx-autonomy属性 if (Vframe_Vframes[tempId = begin.id] || ((selectorObject = Body_RangeVframes[tempId = begin['@{node#owner.vframe}']]) && selectorObject[begin['@{node#guid}']] == 1)) { selectorVfId = tempId; break; } vfs.push(begin); } for (info of vfs) { if (!(tempId = Body_RangeVframes[selectorVfId])) { tempId = Body_RangeVframes[selectorVfId] = {}; } selectorObject = info['@{node#guid}'] || (info['@{node#guid}'] = ++Body_Guid); tempId[selectorObject] = 1; info['@{node#owner.vframe}'] = selectorVfId; } } //if (selectorVfId != G_HashKey) { //从最近的vframe向上查找带有选择器事件的view /*#if(modules.layerVframe){#*/ //主要兼容服务端输出,不带id的情况 let findParent = match && !match.v; /*#}#*/ begin = current.id; if (Vframe_Vframes[begin]) { /* 如果当前节点是vframe的根节点,则把当前的vf置为该vframe 该处主要处理这样的边界情况 <mx-vrame src="./test" mx-click="parent()"/> //.test.js export default Magix.View.extend({ '$<click>'(){ console.log('test clicked'); } }); 当click事件发生在mx-vframe节点上时,要先派发内部通过选择器绑定在根节点上的事件,然后再派发外部的事件 */ backtrace = selectorVfId = begin; } do { vf = Vframe_Vframes[selectorVfId]; if (vf && (view = vf['@{vframe#view.entity}'])) { selectorObject = view['@{view#selector.events.object}']; eventSelector = selectorObject[eventType]; if (eventSelector) { for (begin = eventSelector.length; begin--;) { tempId = eventSelector[begin]; selectorObject = { r: tempId, v: selectorVfId, n: tempId }; if (tempId) { /* 事件发生时,做为临界的根节点只能触发`$`绑定的事件,其它事件不能触发 */ if (!backtrace && G_TargetMatchSelector(current, tempId)) { eventInfos.push(selectorObject); } } else if (backtrace) { eventInfos.unshift(selectorObject); } } } //防止跨view选中,到带模板的view时就中止或未指定 /*#if(modules.layerVframe){#*/ if (findParent) { if (match.v) { eventInfos.push({ ...match, v: selectorVfId }); } else { match.v = selectorVfId; } } /*#}#*/ if (view.tmpl && !backtrace) { /*#if(!modules.layerVframe){#*/ if (match && !match.v) match.v = selectorVfId; /*#}#*/ break; //带界面的中止 } backtrace = 0; } } while (vf && (selectorVfId = vf.pId)); //} } if (match) { eventInfos.push(match); } return eventInfos; }; let Body_DOMEventProcessor = domEvent => { let { target, type } = domEvent; let eventInfos; let ignore; let vframe, view, eventName, fn; let lastVfId; let params, arr = []; while (target != G_DOCBODY) { eventInfos = Body_FindVframeInfo(target, type); if (eventInfos.length) { arr = []; for (let { v, r, n, i } of eventInfos) { if (!v && DEBUG) { return Magix_Cfg.error(Error(`bad ${type}:${r}`)); } if (lastVfId != v) { if (lastVfId && domEvent.isPropagationStopped()) { break; } lastVfId = v; } vframe = Vframe_Vframes[v]; view = vframe && vframe['@{vframe#view.entity}']; if (view) { eventName = n + G_SPLITER + type; fn = view[eventName]; if (fn) { domEvent.eventTarget = target; params = i ? G_ParseExpr(i, view['@{view#updater}']['@{updater#ref.data}']) : {}; domEvent[G_PARAMS] = params; G_ToTry(fn, domEvent, view); //没发现实际的用途 /*if (domEvent.isImmediatePropagationStopped()) { break; }*/ } if (DEBUG) { if (!fn) { //检测为什么找不到处理函数 if (eventName[0] == '\u001f') { console.error('use view.wrapEvent wrap your html'); } else { console.error('can not find event processor:' + n + '<' + type + '> from view:' + vframe.path); } } } } else {//如果处于删除中的事件触发,则停止事件的传播 domEvent.stopPropagation(); } if (DEBUG) { if (!view && view !== 0) { //销毁 console.error('can not find vframe:' + v); } } } } /*|| e.mxStop */ if (((ignore = Body_RangeEvents[fn = target['@{node#owner.vframe}']]) && (ignore = ignore[target['@{node#guid}']]) && ignore[type]) || domEvent.isPropagationStopped()) { //避免使用停止事件冒泡,比如别处有一个下拉框,弹开,点击到阻止冒泡的元素上,弹出框不隐藏 //如果从某个节点开始忽略某个事件的处理,则如果缓存中有待处理的节点,把这些节点owner.vframe处理成当前节点的owner.vframe if (arr.length) { arr.push(fn); } break; } else { //如果某个节点是view临界节点 //先追加id,后续节点的owner.vframe则是该节点 lastVfId = target.id; if (Vframe_Vframes[lastVfId]) { arr.push(lastVfId); } //缓存 arr.push(target); } target = target.parentNode || G_DOCBODY; } if ((fn = arr.length)) { ignore = G_HashKey; for (; fn--;) { view = arr[fn]; if (view.nodeType) { if (!(eventInfos = Body_RangeEvents[ignore])) { eventInfos = Body_RangeEvents[ignore] = {}; } lastVfId = view['@{node#guid}'] || (view['@{node#guid}'] = ++Body_Guid); if (!(params = eventInfos[lastVfId])) { params = eventInfos[lastVfId] = {}; //view['@{node#owner.vframe}'] = ignore; } params[type] = 1; } else { ignore = view; } } } }; let Body_DOMEventBind = (type, searchSelector, remove) => { let counter = Body_RootEvents[type] | 0; let offset = (remove ? -1 : 1); if (!counter || remove === counter) { // remove=1 counter=1 G_DOMEventLibBind(G_DOCBODY, type, Body_DOMEventProcessor, remove); } Body_RootEvents[type] = counter + offset; if (searchSelector) { //记录需要搜索选择器的事件 Body_SearchSelectorEvents[type] = (Body_SearchSelectorEvents[type] | 0) + offset; } };