不要把 JSBridge 写成 if else:Dispatcher、Registry 和 Biz/Imp 分层

不要把 JSBridge 写成 if else:Dispatcher、Registry 和 Biz/Imp 分层

项目地址:github.com/lichenyang5...

本文是 MiniAppRuntime-Harmony 系列的第四篇,重点讲 action 分发和能力分层。

1. 最容易写坏的地方

当 H5 调用 ArkTS 以后,最容易写出这样的代码:

ts 复制代码
if (request.action === 'ui.showToast') {
  // showToast
} else if (request.action === 'system.clipboard.writeText') {
  // clipboard
} else if (request.action === 'network.request') {
  // request
}

一开始没问题。

但是 action 一多,这个文件就会变成灾难。

所以从第二个阶段开始,我就没有继续在 BridgeController 里写业务逻辑,而是拆出了:

text 复制代码
BridgeDispatcher
HandlerRegistry
RuntimeBootstrap
ToastBiz
ToastImp

2. BridgeController 只做通信边界

BridgeController 的职责应该很轻:

text 复制代码
接收 raw message
→ 解析 BridgeRequest
→ 调用 Dispatcher
→ 调用 CallbackExecutor 回调 H5

它不应该知道 ui.showToast 怎么实现。

也不应该知道 promptAction.showToast 怎么调用。

否则 Controller 会变得越来越重。

3. BridgeDispatcher 负责分发

Dispatcher 做的事情很简单:

ts 复制代码
const handler = registry.get(request.action)

if (!handler) {
  return unknownActionResponse
}

try {
  return await handler(request)
} catch (err) {
  return internalErrorResponse
}

它解决两个问题:

  1. 已注册 action,交给对应 handler;
  2. 未注册 action,统一返回 UNKNOWN_ACTION。

这样所有 action 都走同一个入口。

4. HandlerRegistry 负责注册

Registry 只负责三件事:

text 复制代码
register(action, handler)
get(action)
has(action)

它不做业务逻辑,不解析 request,不回调 H5。

这点很关键。

一个好的 Registry 应该像电话簿:

只告诉你某个 action 对应哪个 handler,不负责 handler 具体做什么。

5. RuntimeBootstrap 负责启动注册

RuntimeBootstrap 在运行时初始化时注册内置能力。

例如:

ts 复制代码
registry.register(ACTION_UI_SHOW_TOAST, async (request) => {
  return await toastBiz.handle(request)
})

这让能力注册集中在一个地方。

以后新增 API 时,只需要:

  1. 新增 ActionName;
  2. 新增 Biz;
  3. 新增 Imp;
  4. 在 RuntimeBootstrap 注册 handler。

6. 为什么还要 Biz / Imp

ui.showToast 看起来很简单:

ts 复制代码
promptAction.showToast({ message })

那为什么还要拆成 ToastBiz 和 ToastImp?

因为它们职责不同。

ToastBiz 负责业务语义:

text 复制代码
message 是否存在?
message 是否是字符串?
message 是否为空?
错误码怎么返回?
成功响应怎么组装?

ToastImp 负责系统调用:

text 复制代码
调用 promptAction.showToast

这叫做把"业务判断"和"平台能力"分开。

现在只有 Toast,看不出价值。

等后面有 clipboard、storage、network、device 等 API 时,这种分层就非常有用了。

7. ui.showToast 的完整链路

当前 ui.showToast 的完整链路如下:

text 复制代码
H5
→ window.myascf.send("ui.showToast", { message })
→ JavaScriptProxy.postMessage
→ BridgeController.handleMessage
→ BridgeDispatcher.dispatch
→ HandlerRegistry.get("ui.showToast")
→ RuntimeBootstrap 注册的 handler
→ ToastBiz.handle
→ ToastImp.showToast
→ promptAction.showToast
→ BridgeResponse
→ BridgeCallbackExecutor.sendResponse
→ runJavaScript
→ H5 Promise resolve

这条链路虽然比直接调用长,但它更像一个框架。

每一层都很薄,每一层都好测试,每一层都可以替换。

8. 错误处理

目前已经有几类错误:

text 复制代码
UNKNOWN_ACTION   action 未注册
PARAM_ERROR      参数错误
INTERNAL_ERROR   ArkTS 内部异常
TIMEOUT          H5 等待超时
CALLBACK_LOST    H5 callback 已不存在

例如参数错误:

js 复制代码
window.myascf.send("ui.showToast", {})

不会调用 ToastImp,而是直接返回 PARAM_ERROR。

这说明参数校验在 Biz 层生效了。

9. 这一阶段的意义

到这里,项目已经不是一个简单 Demo。

它具备了运行时框架的基本结构:

text 复制代码
通信边界
能力分发
能力注册
业务校验
平台调用
统一回调
统一错误

后续要新增第二个 API,就不需要改主链路,只需要按模式添加能力即可。

项目地址:

text 复制代码
https://github.com/lichenyang5/MiniAppRuntime-Harmony
相关推荐
猫猫不是喵喵.21 分钟前
Vue3 Props 属性
前端·javascript·vue.js
醉城夜风~1 小时前
CSS元素显示模式(display)
前端·css
AI大模型-小华2 小时前
Codex 三方充值快速入门指南
java·前端·数据库·chatgpt·ai编程·codex·chatgpt pro
做前端的娜娜子4 小时前
同一链接实现 PC Web 与移动 H5 自适应
前端·掘金·金石计划
小帅不太帅4 小时前
架构没变、规模没变,DeepSeek V4 Flash 正式版凭什么暴涨 47 分?
前端·aigc·deepseek
jarvisuni5 小时前
DeepSeekFlash前端依旧拉垮,而且变慢了很多!
前端·javascript·算法
卷福同学6 小时前
AI编程出海第二步:验证关键词能否做站
前端·人工智能·后端
赵庆明老师6 小时前
Vben精讲:21-详解web-antd:tsconfig.json
前端·json·vim
wc887 小时前
微软EDGE浏览器功能学习
前端·学习·edge