前言

说来惭愧,用 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. 394. Decode String 解码icc字符串3[i2[c]]

    [抄题]: Given an encoded string, return it's decoded string. The encoding rule is: k[encoded_string], ...

  2. UI动画优化技巧

    知乎上一篇比较好的文章,分享一下: tabs slide 内容过渡动画 好的动画会淡化页面直接的过度. 但更好的做法是使用连续的动画来来过度内容 当我们在设计交互式选项卡或弹出式菜单的时候,尝试将内容 ...

  3. Java 后台验证的工具类

    Java 后台验证的工具类 public class ValidationUtil {         //手机号     public static String mobile = "^( ...

  4. vue 高阶 provide/inject

    1.一般情况使用都是在app.vue配置为: provide () {return {isTest: this}}, 2.所有子组件都可以引用 拿到app.vue里面的所有数据 inject: ['i ...

  5. Java 接口多继承

    按照理解,一般都是说Java 类是单继承,但可以实现多个接口.但是可以通过接口来实现类的多继承.(如何通过接口来实现多继承???) 那么就一直以为Java里面是单继承,今天看FutureTask源码的 ...

  6. android中一些特殊字符的使用(如:←↑→↓等箭头符号)

    在项目中,有时候在一些控件(如Button.TextView)中要添加一些符号,如下图所示:                         这个时候可以使用图片的方式来显示,不过这些可以直接使用Un ...

  7. (百度)centos7上安装apache指南

    https://jingyan.baidu.com/album/c843ea0bb5ff3977931e4a14.html?picindex=1 原文就不拷贝了.留个网址

  8. [字符串][NOIP2012]Vigenère密码

    Vigenère密码 题目描述 16世纪法国外交家Blaise de Vigenère设计了一种多表密码加密算法——Vigenère密码.Vigenère密码的加密解密算法简单易用,且破译难度比较高, ...

  9. [SoapUI] 检查测试步骤的类型或者或者某种特定类型的步骤列表

    SoapUI Groovy : Check if test step is of specific type, such as : Wsdl, Rest, Jdbc, HTTP, Groovy etc ...

  10. 分布式协议学习笔记(三) Raft 选举自编写代码练习

    由于时间安排上的原因,这次的代码写的稍微有些简略,只能算是自己对RAFT协议的一个巩固. 实现定义2个节点,使用读取配置文件来获取IP和端口以及节点ID 网络使用boost同步流程 一个线程收 一个线 ...