事件系统(钩子糖 / emit / messaging / watch)
事件分四类:
- 钩子糖:
onAttach / onPageChange / onNetwork …,高频生命周期事件的快捷注册; - 基础事件:
on(name, fn)订阅宿主推给本层的事件; - 跨层消息 messaging:带发送者元信息的三层广播;
- 共享存储 watch:宿主持久化存储的变更通知。
本页你将学到
- 钩子糖全表与每个钩子的用法;
- 顶层
emit的真实投递范围(同插件面板事件); on('network')魔法为何移除、用什么替代;- messaging 回声过滤的标准写法。
基础事件 API
on(name, fn)
订阅事件。
- 签名:
on(name, fn) → 本层全局对象(可链式) - 每层全局变量都有;事件名与载荷见下方表。
js
wadLogicLayer.on('pageChange', function (p) {
wadLogicLayer.log(p.route);
});off(name, fn)
取消订阅。要传同一个函数引用。
js
function h(p) { /* … */ }
wadLogicLayer.on('pageChange', h);
wadLogicLayer.off('pageChange', h);emit(name, data)
广播同插件面板事件:逻辑层 / 渲染层 / 面板发出的事件,由本插件的面板通过 wadPanelLayer.on 接收。data 必须可 JSON 序列化。
js
// 逻辑层发
wadLogicLayer.emit('scan-state', { done: 42 });
// 面板收
wadPanelLayer.on('scan-state', function (data) { render(data.done); });注意点:
- 顶层
emit≠ 跨层广播。跨层互通只有messaging; - 面板层
emit同样落在「面板事件」范围(多面板同步用); textEditIntent是宿主保留事件名:逻辑层emit('textEditIntent', …)会被宿主截获为意图(见 生命周期与回调)。
生命周期钩子糖
每个钩子都返回解绑函数,不需要保存函数引用:
| 钩子 | 可用层 | 触发时机 | 载荷 |
|---|---|---|---|
onReady(fn) | 三层 | 就绪:逻辑/渲染注入完成;面板总线连上 | appid |
onAttach(fn) | 逻辑/渲染 | 每次注入(安装/更新/启停/白名单变化/换页重注入都重发) | 逻辑层 appid;渲染层 appid 字符串;面板用 on('attach'),载荷 {appid, version} |
onDetach(fn) | 三层 | 停用 / 卸载 / 切小程序 / 全局关(页面导航销毁不派) | 逻辑层 appid;渲染/面板 {} |
onPageChange(fn) | 逻辑层 | 业务页面路由变化 | {route, appid, options} |
onStorageWrite(fn) | 逻辑层 | 业务调用 wx storage 写 API | {op, key, value?, plugin} |
onNetwork(fn) | 逻辑层 | 捕获管线产出网络摘要 | {id, url, method, status, kind, at, …} |
onOpen(fn) / onClose(fn) | 面板 | 面板打开 / 关闭 | appid;onClose 无载荷 |
js
// 逻辑层:一次性初始化 + 路由监控
wadLogicLayer.onAttach(function (appid) {
wadLogicLayer.log('注入到 ' + appid);
// 这里做幂等初始化(hook、注册技能等)
});
wadLogicLayer.onPageChange(function (p) {
wadLogicLayer.log('路由: ' + p.route + ' 参数: ' + JSON.stringify(p.options));
});
// detach 里必须还原 hook / 清理定时器
wadLogicLayer.onDetach(function (appid) {
wadLogicLayer.log('从 ' + appid + ' 卸载');
});attach = 每次注入
attach 不是一次性事件:配置变化、换页重注入都会重发。初始化要幂等,清理逻辑放 onDetach。完整时序事实与投递矩阵见 生命周期与回调。
storageWrite 监控
js
// 插件自己命名空间(__wad_ 前缀)的写入不触发,防自反馈
wadLogicLayer.onStorageWrite(function (e) {
if (e.key.indexOf('token') >= 0) {
wadLogicLayer.log('token 被写入: ' + e.value);
}
});onNetwork:替代 on('network') 魔法
on('network') 的「订阅即自动开捕获」魔法已移除。订阅网络摘要统一走钩子或 capture:
| 层 | 写法 |
|---|---|
| 逻辑层 | wadLogicLayer.onNetwork(fn) 或 wadLogicLayer.capture.watch(fn) |
| 面板 | wadPanelLayer.capture.watch(fn)(直连捕获管线,最快) |
| 渲染层 | 无原生订阅(用 messaging 从面板转发) |
js
const off = wadLogicLayer.onNetwork(function (e) {
if (e.status >= 400) wadLogicLayer.log('请求失败 ' + e.status + ': ' + e.url.slice(0, 60));
});textEditIntent:逻辑层 → 渲染层意图通道
宿主内置的「意图」通道:逻辑层发出意图后由宿主缓存,并在每次渲染层注入时直推——面板关闭、切页、后退到缓存页都会收敛到最新意图(专为跨页面会话级开关设计)。意图泵 800ms tick,详见 生命周期与回调。
js
// 逻辑层:发意图({on, block} 结构由插件自定义,推荐布尔开关)
var enabled = false;
function pub() { wadLogicLayer.emit('textEditIntent', { on: enabled, block: true }); }
wadLogicLayer.onAttach(function () { enabled = false; pub(); }); // 新会话回到关闭js
// 渲染层:两种接收方式(任选其一)
// ① 事件方式:宿主把意图按 'textEditIntent'(也广播一份 'intent')派发给本层 handler
wadRenderLayer.on('textEditIntent', function (payload) {
if (payload.on) enableEditing(); else disableEditing();
});
// ② 直推入口:宿主泵(800ms)调 globalThis.__wadApplyIntents({插件id: 意图})
globalThis.__wadApplyIntents = function (map) {
var it = map && map[wadRenderLayer.id];
if (it) setEditing(!!it.on, it.block !== false);
};messaging(跨层消息,带发送者元信息)
| 方法 | 签名 | 说明 |
|---|---|---|
messaging.emit | emit(name, data) → Promise<payload> | 广播到当前小程序的全部三层(跨插件可见) |
messaging.on | on(name, fn(d, meta)) | 订阅 |
messaging.once | once(name, fn) | 订阅一次后自动解绑 |
messaging.off | off(name, fn) | 解绑 |
messaging.onAny | onAny(fn(name, data, meta)) → unbind | 全量监听 |
meta 字段:
| 字段 | 说明 |
|---|---|
from | 发送层:'logic' | 'render' | 'panel' |
instanceId | 发送实例(逻辑层恒 'logic';渲染层为本次注入的 render-xxxxxx;面板为 clientId) |
plugin | 发送插件 id(跨插件可见,用消息名加前缀防撞名) |
eventId | 面板 emit 为宿主生成 m<seq>;逻辑层保留 me<seq>-<ts>;渲染层本地返回值与广播副本 id 不同 |
at | 宿主广播时间戳 |
广播不会自动排除发送者自己——接收方自行过滤回声:
js
// 逻辑层发
wadLogicLayer.messaging.emit('cart:updated', { count: 3 });
// 面板收(过滤掉别的插件和自己的回声)
wadPanelLayer.messaging.on('cart:updated', function (data, meta) {
if (meta.plugin !== wadPanelLayer.id) return; // 只听自己插件的
if (meta.from === 'panel') return; // 过滤本层回声
renderCount(data.count);
});
// 全量监听(调试用)
var unbind = wadLogicLayer.messaging.onAny(function (name, data, meta) {
wadLogicLayer.log('[msg]', name, meta.from, JSON.stringify(data));
});注意点:
emit返回 Promise<payload>(resolve 发出的载荷);data 必须可 JSON 序列化;once与off都要拿同一个函数引用才能解绑;- 消息名建议
插件前缀:事件名(如texteditor:escape); - 完整规范见 messaging。
sharedStorage.watch(存储变更通知)
跨层共享存储每次写入,宿主都会向本插件的全部层推一条变更:
js
// 任一层订阅;key 传 '*' 监听全部
var unwatch = wadLogicLayer.sharedStorage.app.watch('lastRoute', function (value, change) {
// value = 新值
// change = {key, op, scope, value, rev, at, plugin, from, instanceId}
wadLogicLayer.log('lastRoute 变成 ' + value + '(来自 ' + change.from + ' 层)');
});
// 解除订阅
unwatch();change 对象字段与完整 API 见 存储;共享状态的更多实践见 sharedStorage。