前言

说来惭愧,用 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"]

我们关注一下 stopPropagationpreventDefault 这两个在网页中常用的方法。从打印得到的属性来看,在小程序中的 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做了以下几件事情:

  1. 从根实例开始,根据comkey找出事件处理函数所在的mpvue实例(getVm)

  2. 通过遍历找到的vm的vnode,结合eventid找到小程序事件对应的真实的事件处理函数(getHandle)

  3. 将小程序的event对象包装为mpvue的event对象(getWebEventByMP),并添加了preventDefault和stopPropagation方法

  4. 将包装后的event对象传给真实的处理函数进行调用

这就解释了生成的wxml中绑定事件的节点都有event-comkeyevent-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的事件代理系统的更多相关文章

  1. JavaScript事件代理和委托(Delegation)

    JavaScript事件代理 首先介绍一下JavaScript的事件代理.事件代理在JS世界中一个非常有用也很有趣的功能.当我们需要对很多元素添加事件的时候,可以通过将事件添加到它们的父节点而将事件委 ...

  2. Atitit事件代理机制原理 基于css class的事件代理

    Atitit事件代理机制原理 基于css class的事件代理 1.1. 在javasript中delegate这个词经常出现,看字面的意思,代理.委托1 1.2. 事件代理1 1.3. 代理标准化规 ...

  3. Atitit.atiagent  agent分销系统 代理系统 设计文档

    Atitit.atiagent  agent分销系统 代理系统 设计文档 1. 启动项目1 2. 首也2 3. 登录功能2 4. 用户中心2 5. 充值查询3 6. 授权下级代理4 7. 我的提成5 ...

  4. js中的事件委托或是事件代理详解

    起因: 1.这是前端面试的经典题型,要去找工作的小伙伴看看还是有帮助的: 2.其实我一直都没弄明白,写这个一是为了备忘,二是给其他的知其然不知其所以然的小伙伴们以参考: 概述: 那什么叫事件委托呢?它 ...

  5. js事件代理(委托)

    JavaScript事件代理(委托)一般用于以下情况: 1. 事件注册在祖先级元素上,代理其子级元素.可以减少事件注册数量,节约内存开销,提高性能. 2. 对js动态添加的子元素可自动绑定事件. 之前 ...

  6. JS 事件代理

    事件处理器:onclick.onmouseover.... 在传统的事件处理中,你需要为每一个元素添加或者是删除事件处理器.然而,事件处理器将有可能导致内存泄露或者是性能下降——你用得越多这种风险就越 ...

  7. jquery事件代理

    在jQuery中,事件代理是指:把事件绑定到父级元素,然后等待事件通过DOM冒泡到该元素时再执行. 在事件侦听过程中有两种触发事件的方式:事件捕获和事件冒泡.事件冒泡更快,效率更高. 事件捕获:事件在 ...

  8. 封装Js事件代理方法

    // 封装事件代理 function delegateEvent(element, tag, event, listener) { // 判断是否支持addEventlistener if(eleme ...

  9. js事件代理

    需要注意的blog:http://blog.csdn.net/majian_1987/article/details/8591385 一篇博客看懂  http://blog.csdn.net/maji ...

随机推荐

  1. 【转载】Sql Server参数化查询之where in和like实现详解

    文章导读 拼SQL实现where in查询 使用CHARINDEX或like实现where in 参数化 使用exec动态执行SQl实现where in 参数化 为每一个参数生成一个参数实现where ...

  2. TCP编程

    Socket是网络编程的一个抽象概念,通常我们用一个Socket表示“打开了一个网络连接”,而打开一个Socket需要知道目标计算机的IP地址和端口号,在指定协议类型即可. 客户端 大多数连接就是考的 ...

  3. 从 NCBI 批量下载基因组的方法

    先下载 assembly summary files The assembly_summary files report metadata for the genome assemblies on t ...

  4. linux python 安装到用户目录

    在公司服务器中,python可能存在多个版本,而且python中的包也有多个不同版本,由于不同猿的需求不同,经常会引起程序冲突,影响工作效率.因此,给大家分享一个在没有root权限时,将python安 ...

  5. 【ZooKeeper】ZooKeeper安装及简单操作

    ZooKeeper介绍 ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件.它是一个为分布式应用提供一 ...

  6. ZOJ 3229 Shoot the Bullet (有源有汇有上下界最大流)

    题意:一个人要给女孩子们拍照,一共 n 天,m 个女孩子,每天他至多拍 d[i] 张照片,每个女孩子总共要被至少拍 g[i] 次.在第 i 天,可以拍 c[i] 个女孩子,c[i] 个女孩子中每个女孩 ...

  7. st-link调试和下载程序(待写)

    st-link调试只用三根线 GND SWCLK SWDIO

  8. 展示博客---Alpha版本展示

    Alpha版本展示 1. 团队成员的简介和个人博客地址,团队的源码仓库地址. 成员 简介 个人博客地址 祁泽文 被动态统计图搞扒下的我 http://www.cnblogs.com/jiaowoxia ...

  9. OpenCV库框架结构

    在上文已经学习过了opencv的编码规则,为了能够方便灵活的运用OPECV库,我们需要对其框架结构进行学习了解,以方便我们进行实际工程调用调用. 1.Opnecv库到底提供了什么? 打开opencv源 ...

  10. js五道经典练习题--第五道成绩列表

    <!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title>& ...