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(), andpreventDefault(). - mpvue event: Exposes 9 fields —
mp(original mini-program event),type,timeStamp,x,y,target,currentTarget, plus stubs forstopPropagationandpreventDefault.
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:
- Resolving the correct Vue instance using
comkey(a hierarchical key derived from component scope). - Locating the registered handler(s) via
eventidand vnode metadata. - Wrapping the native event into an mpvue-compatible object via
getWebEventByMP(). - 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.