浅析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来阻止冒泡。(完)
浅析mpvue的事件代理系统的更多相关文章
- JavaScript事件代理和委托(Delegation)
JavaScript事件代理 首先介绍一下JavaScript的事件代理.事件代理在JS世界中一个非常有用也很有趣的功能.当我们需要对很多元素添加事件的时候,可以通过将事件添加到它们的父节点而将事件委 ...
- Atitit事件代理机制原理 基于css class的事件代理
Atitit事件代理机制原理 基于css class的事件代理 1.1. 在javasript中delegate这个词经常出现,看字面的意思,代理.委托1 1.2. 事件代理1 1.3. 代理标准化规 ...
- Atitit.atiagent agent分销系统 代理系统 设计文档
Atitit.atiagent agent分销系统 代理系统 设计文档 1. 启动项目1 2. 首也2 3. 登录功能2 4. 用户中心2 5. 充值查询3 6. 授权下级代理4 7. 我的提成5 ...
- js中的事件委托或是事件代理详解
起因: 1.这是前端面试的经典题型,要去找工作的小伙伴看看还是有帮助的: 2.其实我一直都没弄明白,写这个一是为了备忘,二是给其他的知其然不知其所以然的小伙伴们以参考: 概述: 那什么叫事件委托呢?它 ...
- js事件代理(委托)
JavaScript事件代理(委托)一般用于以下情况: 1. 事件注册在祖先级元素上,代理其子级元素.可以减少事件注册数量,节约内存开销,提高性能. 2. 对js动态添加的子元素可自动绑定事件. 之前 ...
- JS 事件代理
事件处理器:onclick.onmouseover.... 在传统的事件处理中,你需要为每一个元素添加或者是删除事件处理器.然而,事件处理器将有可能导致内存泄露或者是性能下降——你用得越多这种风险就越 ...
- jquery事件代理
在jQuery中,事件代理是指:把事件绑定到父级元素,然后等待事件通过DOM冒泡到该元素时再执行. 在事件侦听过程中有两种触发事件的方式:事件捕获和事件冒泡.事件冒泡更快,效率更高. 事件捕获:事件在 ...
- 封装Js事件代理方法
// 封装事件代理 function delegateEvent(element, tag, event, listener) { // 判断是否支持addEventlistener if(eleme ...
- js事件代理
需要注意的blog:http://blog.csdn.net/majian_1987/article/details/8591385 一篇博客看懂 http://blog.csdn.net/maji ...
随机推荐
- [leetcode]170. Two Sum III - Data structure design两数之和III - 数据结构设计
Design and implement a TwoSum class. It should support the following operations: add and find. add - ...
- java_22 Map接口
1Map Collection是孤立存在的,向集合中存储元素是一个一个放进去的 Map中的集合存储是成对的,可以通过键找到值.即将键映射到值的对象.一个映射不能包含重复的键:每个键最多只能映射到一个值 ...
- etcd-v2第三集
简单说下golang的etcd接口例子.etcd api有v2(http+json)和v3(grpc)两个版本,目前大家都用v2,所以... v2: https://github.com/coreos ...
- Android通过手机搭建服务器,WIFI建立热点实现C/S聊天室通信功能
应用效果图: 客户端 ...
- MySQL表与表之间的关系详解
外键 说到表与表之间的关系就不得不说到一个关键词:外键 MySQ中的外键是什么,和表与表之间有什么关联? 外键(foreign key)又叫外连接, 在数据库中发挥着重要的作用 尤其是对于表和表之间的 ...
- 20172306 2018-2019《Java程序设计与数据结构课堂测试补充报告》
学号 2017-2018-2 <程序设计与数据结构>课堂测试补充报告 课程:<程序设计与数据结构> 班级: 1723 姓名: 刘辰 学号:20172306 实验教师:王志强 必 ...
- TaxonKit - A cross-platform and Efficient NCBI Taxonomy Toolkit
https://github.com/0820LL/taxonkit Usage: https://bioinf.shenwei.me/taxonkit/usage/
- How to configure Samba Server share on Debian 9 Stretch Linux
Lubos Rendek Debian 13 June 2017 Contents 1. Objective 2. Operating System and Software Versions 3. ...
- powerDesigner 正向工程生成sql注释
找到script-->objects-->column-->add value内容如下: %:COLUMN% %:DATATYPE%[.Z:[%Compressed%? compre ...
- python参数
1.形参变量和实参 形参变量:只有在被调用时才分配内存单元,在调用结束时,即释放所分配的内存单元,因此,形参只在函数内有效,函数调用结束返回主调用函数后则不能再使用该形参变量. 实参:可以是常量,变量 ...