三层 API 对比
一个插件可以在三个上下文里运行代码:逻辑层(appservice)、渲染层(页面 WebView)、面板(浮窗网页)。三个上下文的全局变量不同——wadLogicLayer、wadRenderLayer、wadPanelLayer,同名能力在三层的可用性与同步/异步行为也不同。本页是每个 API 在三层的完整对照。
本页你将学到
- 每个 API 在三层的可用性与同步/异步差异;
- 哪些调用是本地直调、哪些走跨层 Promise;
- 层级越界时报什么错(同步抛 vs Promise.reject)。
图例:
| 图例 | 含义 |
|---|---|
| ✅ | 本层本地可用 |
| 🔁 | 跨层调用(返回 Promise) |
| 👁 | 只读观察 |
| ✋ | 同步抛 E_LAYER_ONLY |
| ❌ | Promise.reject(E_LAYER_ONLY) |
| — | 不存在 |
元信息(三个全局共有)
| API | 逻辑层 | 渲染层 | 面板层 | 说明 |
|---|---|---|---|---|
apiVersion | ✅ '1.0' | ✅ '1.0' | ✅ '1.0' | API 版本 |
layer | ✅ 'logic' | ✅ 'render' | ✅ 'panel' | 当前层名 |
id / name / version | ✅ | ✅ | ✅ | 面板 version 是 SDK '1.0',插件版本用 instance().version |
appid | ✅ | ✅ | ✅ | 当前小程序 appid |
instanceId | ✅ 'logic' | ✅ 'render-xxxxxx' | ✅ | 实例标识 |
ready | ✅ 注入完成兑现 | ✅ | ✅ 总线连上兑现 | 就绪 Promise |
instance() | ✅ | ✅ | ✅ | 实例快照;逻辑/渲染含 page,面板无 page |
log(...) | ✅ | ✅ | ✅ | 插件日志(面板日志页可见) |
clientId | — | — | ✅ | 面板总线客户端 id(多面板区分) |
业务面(page / register / call / eval)
| API | 逻辑层 | 渲染层 | 面板层 |
|---|---|---|---|
page() | ✅ 同步 {route, options} | ✅ 同步 {url, title} | 🔁 wadPanelLayer.logicLayer.page() |
register(n, fn) | ✅ | — | — |
call(n, ...args) | ✅ 同步直调(可变参) | 🔁 wadRenderLayer.logicLayer.call() | 🔁 wadPanelLayer.logicLayer.call() |
eval(expr) | ⛔ E_EVAL_SANDBOX(沙箱禁用) | ✅ 同步(页面上下文) | 🔁 logicLayer.eval() → Promise(需 CDP;关闭时 CDP_DISABLED) |
call 统一可变参:call('add', 1, 2)。数组就是一个普通参数——call('add', [1, 2]) 传的是「一个数组参数」,不是两个参数。
DOM 与渲染面
| API | 逻辑层 | 渲染层 | 面板层 |
|---|---|---|---|
dom.query / create / text / render | — | ✅ | — |
localStorage / sessionStorage | — | ✅(页面 WebView) | ✅(面板 WebView) |
page() 返回 | ✅ 本地 {route, options};跨层 logicLayer.page() 返回 {route, options, index, appid} | ✅ {url, title} | logicLayer.page() 同上;renderLayer.page() 返回 {url, title} |
asset(p) / assets.* | ✅(script/css ❌) | ✅ 全量 | ✅ 全量 |
screenshot(opts) | 🔁 renderLayer.screenshot() | — | 🔁 renderLayer.screenshot() |
domSnapshot(opts) | 🔁 | — | 🔁 |
targets() | — | — | 🔁 |
存储
| API | 逻辑层 | 渲染层 | 面板层 |
|---|---|---|---|
storage.get/set/remove/keys/clear | ✅ Promise(wx storage) | — | ✅ Promise(面板宿主 JSON) |
storage.*Sync | ✅ 同步 | — | — |
sharedStorage.app / global | ✅ Promise | ✅ Promise | ✅ Promise |
sharedStorage.*.watch(key, fn) | ✅ | ✅ | ✅ |
逻辑层 storage 操作的是小程序 wx storage(插件命名空间 __wad_<id>_);面板 storage 是插件私有宿主 JSON。渲染层没有顶层 storage。
UI
| API | 逻辑层 | 渲染层 | 面板层 |
|---|---|---|---|
ui.toast(t) | ✅ 宿主 Toast(投递) | ✅ 页面内 DOM Toast | ✅ 宿主 Toast |
ui.el / button / card / … 组件库 | — | — | ✅ |
ui.setTitle / close / setSize | — | — | ✅ |
顶层 onOpen(fn) / onClose(fn) | — | — | ✅(不存在 ui.onOpen/ui.onClose) |
事件
| API | 逻辑层 | 渲染层 | 面板层 |
|---|---|---|---|
on(name, fn) / off(name, fn) | ✅ | ✅ | ✅(纯事件) |
emit(name, data) | ✅ 本插件逻辑层事件 | ✅ 同插件面板事件 | ✅ 本插件面板 + 同插件逻辑层事件 |
onReady / onAttach / onDetach | ✅ | ✅ | onReady / onDetach ✅;attach 用 on('attach') |
onPageChange / onStorageWrite / onNetwork | ✅ | — | — |
onOpen / onClose | — | — | ✅ |
顶层 emit 主要投递本插件面板;面板发出的 emit 还会派发到同插件逻辑层。完整跨层广播请用 messaging。
http(统一网络)
| API | 逻辑层 | 渲染层 | 面板层 |
|---|---|---|---|
request(opts) | ✅ wx.request 语义 | ✅ fetch 语义 | ✅ 宿主代理 RPC |
get / post / put / delete | ✅ | ✅ | ✅ |
upload(url, body, opts?) | ✅ | ✅(POST 文本) | ✅ 宿主直传(不占总线帧) |
task.abort() | ✅ 真取消 | ✅ AbortController | no-op(调用不报错,但不会真正中断请求) |
默认 dataType | 'json' | 'json' | 'json' |
| 超时 | 60s | 无内置超时(fetch) | 请求 60s(65s 仅为 RPC 外层) |
统一返回 {statusCode, header, data, errMsg?};非 2xx 不 reject(自己判断 statusCode)。插件 SDK 自身的内部请求带 __wadInternal 标记,不进任何 hook。
messaging(跨层广播)
| API | 逻辑层 | 渲染层 | 面板层 |
|---|---|---|---|
emit(name, data) | ✅ → Promise<payload> | ✅ → Promise<payload> | ✅ → Promise<payload> |
on(name, fn(d, meta)) / off / once | ✅ | ✅ | ✅ |
onAny(fn(name, data, meta)) | ✅ | ✅ | ✅ |
广播范围:所有已连接面板(__messageEvent 不按 appid 过滤,跨插件)+ 当前小程序逻辑层 + 渲染层。meta = {from, instanceId, plugin, eventId, at}——发送者也在接收者里(回声),由接收方按 meta 自行过滤。
sharedStorage(三层共享存储)
| API | 逻辑层 | 渲染层 | 面板层 |
|---|---|---|---|
app.get/set/remove/keys/clear | ✅ Promise | ✅ Promise | ✅ Promise |
global.get/set/remove/keys/clear | ✅ Promise | ✅ Promise | ✅ Promise |
| `app.watch(key | '*', fn) / global.watch` | ✅ | ✅ |
存储位置:plugins/<id>/store/shared.json。值 ≤ 64KB,单作用域 ≤ 1MB。变更对象:{key, op, scope, value, rev, at, plugin, from, instanceId, appid?}。
capture(网络抓包与改写)
| API | 逻辑层 | 渲染层 | 面板层 |
|---|---|---|---|
intercept.request(fn) / response(fn) | ✅ | ✋ 同步抛 | — |
watch(fn) | ✅ | ✋ 同步抛 | ✅ |
records(n?) | ✅ | ❌ reject | ✅ |
body(id) | ✅ | ❌ reject | ✅ |
rules() | ✅ | ❌ reject | ✅ |
addRule(rule) | ✅ → Promise<{id}> | ❌ reject | ✅ → Promise<{id}> |
removeRule(id) | ✅ → Promise | ❌ reject | ✅ → Promise |
clear() | ✅ | ❌ reject | ✅ |
JS hook 与 Network 面板脚本同一套 req/resp 契约,详见 改写请求/响应 与 通过插件 · 网络重写。
native(mmtls/ilink 回调)
| API | 逻辑层 | 渲染层 | 面板层 |
|---|---|---|---|
intercept.request(fn) / response(fn) | ✅ req/resp 对象改写 | ✋ 同步抛 E_LAYER_ONLY | ✋ 同步抛 E_LAYER_ONLY |
observe.request(fn) / response(fn) | 👁 | ✋ 同步抛 | 👁(>180KB 截断;records/get 单条 body >96KB 也截断并标记) |
records(n) / get(id) / clear() | ✅ | ❌ reject | ✅ |
覆盖云网关、云托管、云容器等 mmtls/ilink 交互,详见 通过插件 · Native脚本。
assets(插件资产)
| API | 逻辑层 | 渲染层 | 面板层 |
|---|---|---|---|
url(p) | ✅ | ✅ | ✅ |
text / json / arrayBuffer | ✅ | ✅ | ✅ |
wasm(p, imports) | ✅ | ✅ | ✅ |
script(p) / css(p) | ❌ reject(E_LAYER_ONLY) | ✅ | ✅ |
preload(paths) / clear() | ✅ | ✅ | ✅ |
资产根:/v1/plugins/<id>/file/<相对路径>(zip 安装的子目录按相对路径取)。
tools / debugger
| API | 逻辑层 | 渲染层 | 面板层 |
|---|---|---|---|
tools.* 全量 | ✅ | ✅ | ✅ |
debugger.* | ✅ | — | ✅(另有 debugger.watch(onPaused, ms)) |
跨层代理(谁调谁)
| 代理 | 挂在哪 | 可用成员 |
|---|---|---|
wadLogicLayer.renderLayer | 逻辑层 | eval(投递,不等结果)/ page / screenshot / domSnapshot / ui.toast / tools |
wadLogicLayer.panelLayer | 逻辑层 | ui.toast / tools |
wadRenderLayer.logicLayer | 渲染层 | call(fn, ...args)(Promise)/ emit / ui.toast / tools |
wadRenderLayer.panelLayer | 渲染层 | ui.toast / tools |
wadPanelLayer.logicLayer | 面板 | call / eval / page / storage(逻辑层 wx 存储)/ ui.toast |
wadPanelLayer.renderLayer | 面板 | eval / screenshot / domSnapshot / targets / page / ui.toast |
注意:本层没有自引用代理——逻辑层里没有 wadLogicLayer.logicLayer,渲染层里没有 wadRenderLayer.renderLayer,面板里没有 wadPanelLayer.panelLayer。
错误约定
- 所有跨层调用返回 Promise;失败 reject 的
Error带.code(TIMEOUT | BUS_DISCONNECTED | E_LAYER_ONLY | PLUGINS_DISABLED | CDP_DISABLED | LOGIC_UNAVAILABLE | RENDER_UNAVAILABLE | NOT_FOUND)。 - 层级越界分两种:注册类(如渲染层调
capture.intercept)同步抛E_LAYER_ONLY;查询类(如渲染层调capture.records)返回 Promise.reject。 - 共享存储值超限 reject
value_too_large / scope_too_large。
下一步
- 跨层调用实例集:十几个可直接抄的组合
- sharedStorage / messaging:跨层协作的两个核心
- 生命周期与回调:attach/detach 投递矩阵与钩子糖