Implementing Decoupled Page Communication in WeChat Mini Programs Using a Publish/Subscribe Event Bus
This article explains how to solve page coupling in WeChat Mini Programs by using flag variables, page stack navigation, and finally a publish/subscribe event bus, providing a complete Event class implementation, usage examples, and optimization tips for the Mini Program environment.
Author Introduction
"JDC-Multi‑Terminal R&D Department" launched the "AoTu Lab" brand in October 2015 to showcase the team's front‑end capabilities and technical concepts. The team provides web front‑end development, mini‑program development, mini‑game development, and H5 animation development for the main mall site, WeChat mini‑program, and other business groups.
Page Communication Overview
In WeChat Mini Programs, each Page is an independent module with its own scope, so a communication strategy is needed when the state of one Page changes and other Pages need to be updated.
Imagine a scenario where a user opens an order list page, selects an order, and navigates to the order detail page. When the order status changes on the detail page (e.g., after payment), the list page must be updated accordingly.
To update the list page view, you need to call the setData method of the Page object. Three common solutions are listed below:
1. Set a Flag
The simplest method is to set a flag (e.g., in localStorage or on the global App object) in the success callback of the order operation, and then check the flag in the onShow lifecycle of the list page to decide whether to update and with what parameters.
This works for simple business logic with low coupling, but becomes cumbersome and hard to maintain as complexity grows.
Flowchart:
2. Use Page Stack to Get Page Object
If the detail page can obtain the list page's Page object, it can call its setData method. Mini Programs provide getCurrentPages , which returns the current page stack; the list page can be accessed based on the stack order.
However, this approach tightly couples pages to the stack order, making maintenance difficult when page order changes in future iterations.
Flowchart:
3. Publish/Subscribe Pattern (Optimal Solution)
The publish/subscribe pattern consists of a publisher, multiple subscribers, and a dispatcher. Subscribers register callbacks for specific events; when the publisher emits an event, the dispatcher invokes all registered callbacks.
In iOS, Notification Center and Android's EventBus use this pattern, but WeChat Mini Programs do not provide a built‑in notification mechanism, so we need to implement it manually.
We first create an Event class that holds a collection of callbacks and provides three basic methods: on (subscribe), emit (publish), and off (unsubscribe).
// event.js
class Event {
on(event, fn) {
if (typeof fn != "function") {
console.error('fn must be a function');
return;
}
this._cbs = this._cbs || {};
(this._cbs[event] = this._cbs[event] || []).push(fn);
}
emit(event) {
this._cbs = this._cbs || {};
var callbacks = this._cbs[event], args;
if (callbacks) {
callbacks = callbacks.slice(0);
args = [].slice.call(arguments, 1);
for (var i = 0, len = callbacks.length; i < len; i++) {
callbacks[i].apply(null, args);
}
}
}
off(event, fn) {
this._cbs = this._cbs || {};
if (!arguments.length) {
this._cbs = {};
return;
}
if (!event) return;
var callbacks = this._cbs[event];
if (!callbacks) return;
if (!fn) {
delete this._cbs[event];
return;
}
for (var i = 0, len = callbacks.length; i < len; i++) {
var cb = callbacks[i];
if (cb === fn) {
callbacks.splice(i, 1);
break;
}
}
return;
}
}App is the Mini Program instance; we attach an Event instance to it so every Page can access it via getApp() :
// app.js
const Event = require('./libs/event');
App({
event: new Event(),
// ... other properties
});In the order list page, subscribe to the afterPaySuccess event in onLoad :
// order_list.js
var app = getApp();
Page({
onLoad: function() {
app.event.on('afterPaySuccess', this.afterPaySuccess.bind(this));
},
afterPaySuccess: function(orderId) {
// update the list view with the new order status
},
// ... other methods
});In the order detail page, emit the event after a successful payment:
// order_detail.js
var app = getApp();
Page({
raisePayment: function() {
// ... payment logic
app.event.emit('afterPaySuccess', orderId);
},
// ... other methods
});All Pages should unsubscribe in onUnload to avoid memory leaks:
// any page
var app = getApp();
Page({
onUnload: function() {
// remove all callbacks
app.event.off();
// remove all callbacks for a specific event
app.event.off('afterPaySuccess');
// remove a specific callback
app.event.off('afterPaySuccess', this.afterPaySuccess);
},
// ... other methods
});Because bind creates a new anonymous function, the original callback cannot be found when calling off . To solve this, we modify the Event class to store both the callback and its context, allowing proper removal.
// Updated event.js (core parts)
class Event {
on(event, fn, ctx) {
if (typeof fn != "function") {
console.error('fn must be a function');
return;
}
this._stores = this._stores || {};
(this._stores[event] = this._stores[event] || []).push({ cb: fn, ctx: ctx });
}
emit(event) {
this._stores = this._stores || {};
var store = this._stores[event], args;
if (store) {
store = store.slice(0);
args = [].slice.call(arguments, 1);
for (var i = 0, len = store.length; i < len; i++) {
store[i].cb.apply(store[i].ctx, args);
}
}
}
off(event, fn) {
this._stores = this._stores || {};
if (!arguments.length) { this._stores = {}; return; }
if (!event) return;
var store = this._stores[event];
if (!store) return;
if (!fn) { delete this._stores[event]; return; }
for (var i = 0, len = store.length; i < len; i++) {
var cb = store[i].cb;
if (cb === fn) {
store.splice(i, 1);
break;
}
}
return;
}
}Now the subscription includes the Page object as context, eliminating the need for bind :
// usage after update
app.event.on('afterPaySuccess', this.afterPaySuccess, this);Conclusion
A simple event class can be written in a few dozen lines, but there is plenty of room for optimization and extension. For deeper insights, refer to Node.js's event module: https://github.com/nodejs/node/blob/master/lib/events.js.
We also maintain an issue list for Mini Program development pain points: https://github.com/o2team/wxapp-issue-list/blob/master/issue-list.md. Feel free to contribute.
References
https://github.com/dannnney/weapp-event
--- END ---
JD Tech
Official JD technology sharing platform. All the cutting‑edge JD tech, innovative insights, and open‑source solutions you’re looking for, all in one place.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.