浅析mpvue的事件代理系统
前言
说来惭愧,用 mpvue 大半年,小程序快一年了,居然还试图用
event.stopPropagation方法阻止事件冒泡,也是有点蠢。痛定思痛,写篇博文来认真捋一捋小程序的事件系统和 mpvue 的事件代理
小程序事件系统,mpvue 事件系统和 DOM 事件系统的差别
从文档得知,小程序的 event 对象和 DOM 的 event 对象是不同的,我们还可以通过把两个对象的属性打印出来进行比较:
// 小程序 event 对象属性(8 个) ["type", "timeStamp", "target", "currentTarget", "detail", "touches", "changedTouches", "_requireActive"] // DOM event 对象属性 / 方法(54 个) ["isTrusted", "screenX", "screenY", "clientX", "clientY", "ctrlKey", "shiftKey", "altKey", "metaKey", "button", "buttons", "relatedTarget", "pageX", "pageY", "x", "y", "offsetX", "offsetY", ..., "cancelable", "timeStamp", "srcElement", "returnValue", "cancelBubble", "path", "composedPath", "stopPropagation", "stopImmediatePropagation", "preventDefault", "initEvent"] // mpvue event 对象属性 / 方法(9 个) ["mp", "type", "timeStamp", "x", "y", "target", "currentTarget", "stopPropagation", "preventDefault"]
我们关注一下
stopPropagation和
preventDefault这两个在网页中常用的方法。从打印得到的属性来看,在小程序中的 event 对象中没有这两个方法,取而代之的是借助
catchtap这样的语法来阻止事件冒泡。另外,小程序中没有什么方式来阻止默认事件。
但是在 mpvue 中,event 对象又被挂载了这两个方法。至于这两个方法是否可以真的有用,这就需要我们了解一下 mpvue 的事件代理机制。
mpvue 的事件代理
<cover-view class="_cover-view" id="zanIcon" catchtap="handleProxy" data-eventid="{{'4'}}" data-comkey="{{$k}}">
上面是mpvue生成的wxml文件片段,如果你看过 mpvue 的生成文件,可能会发现,在 mpvue 生成的 wxml 文件中,所有的事件都被一个叫 handleProxy 的函数接管,在 handleProxy 进行处理后再去调用我们写的真正的事件处理函数。这个方法在initMp时,作为小程序Page构造函数的一个选项,从而可以在wxml中被正确调用。函数的定义如下:
// https://github.com/Meituan-Dianping/mpvue/blob/63700b2d4e4e678bc4297c71e3acd1f36647907a/src/platforms/mp/runtime/lifecycle.js#L286-L288 handleProxy (e) { return rootVueVM.$handleProxyWithVue(e) } // https://github.com/Meituan-Dianping/mpvue/blob/63700b2d4e4e678bc4297c71e3acd1f36647907a/src/platforms/mp/runtime/index.js#L53-L54 import { handleProxyWithVue } from './events' Vue.prototype.$handleProxyWithVue = handleProxyWithVue // handleProxyWithVue实现在 events.js 中
从上面的代码可以看出,handleProxy只是将小程序的event对象传给handleProxyWithVue函数进行进一步处理。接下来我们看看handleProxyWithVue都做了什么工作:
// https://github.com/Meituan-Dianping/mpvue/blob/63700b2d4e4e678bc4297c71e3acd1f36647907a/src/platforms/mp/runtime/events.js#L84-L108 export function handleProxyWithVue (e) { const rootVueVM = this.$root const { type, target = {}, currentTarget } = e const { dataset = {}} = currentTarget || target const { comkey = '', eventid } = dataset const vm = getVM(rootVueVM, comkey.split(','))// 根据comkey找到页面对应的mpvue实例(vm) if (!vm) { return } const webEventTypes = eventTypeMap[type] || [type] const handles = getHandle(vm._vnode, eventid, webEventTypes) //找到真正的事件处理函数 if (handles.length) { const event = getWebEventByMP(e) // 包装为mpvue的event对象 if (handles.length === 1) { const result = handles[0](event) return result } handles.forEach(h => h(event))// 调用真实的事件处理函数 } }
handleProxyWithVue做了以下几件事情:
从根实例开始,根据comkey找出事件处理函数所在的mpvue实例(getVm)
通过遍历找到的vm的vnode,结合eventid找到小程序事件对应的真实的事件处理函数(getHandle)
将小程序的event对象包装为mpvue的event对象(getWebEventByMP),并添加了preventDefault和stopPropagation方法
将包装后的event对象传给真实的处理函数进行调用
这就解释了生成的wxml中绑定事件的节点都有
event-comkey和
event-eventid属性,在一个事件触发时,它们是被用来寻找事件对应的vm实例和对应的事件处理函数的。
最后我们来看看getWebEventByMP函数:
// https://github.com/Meituan-Dianping/mpvue/blob/63700b2d4e4e678bc4297c71e3acd1f36647907a/src/platforms/mp/runtime/events.js#L62-L82 function getWebEventByMP (e) { const { type, timeStamp, touches, detail = {}, target = {}, currentTarget = {}} = e const { x, y } = detail const event = { mp: e, type, timeStamp, x, y, target: Object.assign({}, target, detail), currentTarget, stopPropagation: noop, preventDefault: noop } if (touches && touches.length) { Object.assign(event, touches[0]) event.touches = touches } return event }
从上面代码可以看出,stopPropagation和preventDefault都对应到一个叫noop的函数,而这个函数是一个空函数,也就是说在mpvue中,尽管可以调用
event.stopPropagation(),但是并没有什么用,还是要借助
.stop修饰符通过compiler编译成
catchEvent来阻止冒泡。(完)
- 内鬼作祟还是恐怖袭击?——浅析韩国农协银行系统瘫痪事件 推荐
- 浅析javascript中的事件代理
- Android中的 事件流----浅析安卓中的动与静(二) 系统事件流
- UE笔记---绑定代理事件
- python回测系统浅析:事件驱动
- cocos2d-x游戏引擎核心(3.x)----事件分发机制之事件从(android,ios,desktop)系统传到cocos2dx的过程浅析
- 浅析javascript中的事件代理
- js中的事件委托或是事件代理详解
- 系统性能监控系列1:使用JAVA动态代理实现非侵入式的性能测量方法
- 从SiteServer CMS网站管理员权限分配浅析一般权限系统的设计思路
- linux系统服务总结之六:SQUID代理简单上网配置
- [AngularJS面面观] 11. scope事件机制 - 事件系统在Angular框架中的应用
- Android4.1输入系统参数配置和输入事件校准
- Linux操作系统:浅析文件系统
- Qt之事件系统
- javascript中的事件委托(代理)
- Centos系统使用代理上网时 yum的代理设置
- 搜索系统10:机器学习算法浅析
- javaScript事件代理
- 通过CGLIB实现AOP的浅析(顺便简单对比了一下JDK的动态代理)