Understanding mpvue’s Event Delegation and Propagation Handling

mpvue, a framework that enables Vue.js development for WeChat Mini Programs, implements a custom event system built on top of the native mini-program event model. Unlike standard DOM environments, mini-programs lack native stopPropagation and preventDefault methods — instead relying on WXML directives like catchtap to suppress bubbling. mpvue bridges this gap by synthesizing a browser-like event object, but its implementation has important behavioral constraints.

Event Object Differences Across Environments

The event objects in native mini-programs, DOM, and mpvue differ significantly:

  • Mini-program event: Contains only 8 core properties — type, timeStamp, target, currentTarget, detail, touches, changedTouches, and _requireActive.
  • DOM event: Includes over 50 properties and methods, notably stopPropagation(), stopImmediatePropagation(), and preventDefault().
  • mpvue event: Exposes 9 fields — mp (original mini-program event), type, timeStamp, x, y, target, currentTarget, plus stubs for stopPropagation and preventDefault.

Although mpvue attaches these two methods to its synthetic event object, they are no-ops — meaning calling them has zero effect on actual event flow or default behavior.

How mpvue Routes Events

In compiled WXML, every interactive element is rewritten to delegate handling through a centralized proxy:

<button class="btn" catchtap="handleProxy" data-eventid="{{'7'}}" data-comkey="{{$k}}"></button>

All event bindings point to handleProxy, which is injected into the page’s lifecycle options during initialization:

// Simplified runtime logic
Page({
  handleProxy(e) {
    return this.$root.$handleProxyWithVue(e);
  }
});

This triggers $handleProxyWithVue, whose responsibilities include:

  1. Resolving the correct Vue instance using comkey (a hierarchical key derived from component scope).
  2. Locating the registered handler(s) via eventid and vnode metadata.
  3. Wrapping the native event into an mpvue-compatible object via getWebEventByMP().
  4. Invoking the user-defined handler(s) with the wrapped event.

Why stopPropagation() Is a No-op

The wrapping function getWebEventByMP() constructs the event object as follows:

function getWebEventByMP(e) {
  const { type, timeStamp, touches, detail = {}, target = {}, currentTarget = {} } = e;
  const { x, y } = detail;

  const event = {
    mp: e,
    type,
    timeStamp,
    x,
    y,
    target: { ...target, ...detail },
    currentTarget,
    stopPropagation: () => {}, // noop
    preventDefault: () => {}     // noop
  };

  if (touches?.length) {
    Object.assign(event, touches[0]);
    event.touches = touches;
  }

  return event;
}

As shown, both stopPropagation and preventDefault resolve to empty functions. This design reflects mpvue’s architectural choice: true propagation control must happen at the WXML layer — not in side JavaScript handlers.

Correct Way to Prevent Bubbling in mpvue

To halt event bubbling, developers must use Vue-style modifiers at compile time. For example:

<view @tap.stop="onTap">Stop bubbling</view>

The mpvue compiler transforms @tap.stop into catchtap in the output WXML, bypassing the proxy entirely for that event. Similarly, @tap.capture maps to bindtap with capture phase semantics — though mini-program support for capture is limited and inconsistent across versions.

Attempting to emulate this behavior manual — such as calling event.stopPropagation() inside a handler — will not alter event dispatch because the native bubbling has already occurred before the handler runs.

Tags: mpvue weapp mini-program vuejs event-delegation

Posted on Fri, 16 Oct 2026 16:41:56 +0000 by Dang