第 9 章:跨端开发:uni-app、小程序、H5 与多端桥接
本章定位
跨端开发要求理解不同运行容器、编译差异和桥接机制。AI Agent 如果要在小程序、H5 或移动端使用,需要适配流式 UI、工具状态和平台能力。
必须掌握清单
必须掌握
能解释跨端框架解决什么,不能解决什么。
相关答案: 跨端框架统一常见组件、路由、状态和开发工具,减少多平台重复业务代码;它不能消除宿主 API、渲染模型、审核规则、性能和交互规范的差异。平台专属能力仍要经过适配层。
具体例子: 商品列表和登录流程可共享,但小程序支付、H5 浏览器分享、原生推送需要分别实现适配器,再向业务层暴露统一接口。
能区分小程序、H5 和原生 WebView 的能力差异。
相关答案: H5 运行在浏览器标准环境并受同源、浏览器 API 和页面生命周期约束;小程序运行在平台沙箱中,没有完整 DOM,网络和包体受平台限制;WebView 是原生应用嵌入网页,可通过桥接获得原生能力,但安全边界更复杂。
具体例子: H5 可使用标准
EventSource时,小程序可能只能通过平台请求 API 模拟流;WebView 获取相册要调用原生注入的 bridge,并处理应用切后台和页面销毁。能设计 JSBridge 的调用、回调和错误处理。
相关答案: Bridge 应使用唯一请求 ID、明确的方法名与参数 schema,返回 Promise 风格的成功或错误结果;同时处理超时、重复回调、页面销毁、版本协商和来源校验,不能暴露任意原生命令执行能力。
具体例子: Web 调用
bridge.invoke('pickImage', params)后把 resolver 存入requestId映射;原生回传同一 ID 时完成 Promise,超时或卸载则拒绝并清理映射。能处理移动网络下的流式请求和断线重连。
相关答案: 移动网络会频繁切换、休眠和丢包,流式客户端要区分主动取消、临时断网与服务端失败,使用退避重连并携带最后事件位置。重连必须幂等,且应设置最大次数和用户可见状态。
具体例子: 地铁中连接中断后先显示“正在重连”,按 1、2、4 秒退避并携带最后事件 ID;应用从后台恢复时先查询请求状态,再补取缺失 token,而非重复发起生成。
能用条件编译隔离平台差异。
相关答案: 条件编译适合隔离无法在其他平台解析或打包的平台代码,差异块应尽量集中在适配层;业务逻辑仍依赖统一接口,避免各处散落平台判断。
具体例子:
platform/share.ts分别为小程序和 H5 导出相同的share(),条件编译只存在于该文件;商品详情组件调用share(product),无需知道当前平台。
原理讲解
跨端框架统一常见 UI 和生命周期,但无法完全抹平平台能力。小程序没有完整 DOM,SSE 和某些网络能力可能受限;H5 运行在浏览器,JSBridge 通常由 WebView 注入。
条件编译是把平台差异放在显式块中,而不是把不同平台的 if 散落在业务逻辑里。桥接调用需要明确回调、错误和生命周期,避免原生回调到达时页面已经销毁。
代码示例或模板
function sendToNative(
command: string,
payload: unknown,
): Promise<unknown> {
return new Promise((resolve, reject) => {
// #ifdef H5
const native = (window as unknown as { bridge?: { call: Function } }).bridge;
native?.call(command, payload, resolve, reject);
// #endif
// #ifdef MP-WEIXIN
uni.sendSocketMessage?.({ data: JSON.stringify({ command, payload }) });
// #endif
});
}这是桥接接口的模板,实际项目需要处理超时、重复回调和平台未实现能力。
AI Agent 概念对照
- 多端 UI 对应消息和工具状态的一致渲染。
- JSBridge 对应 MCP 客户端与外部工具通信。
- 通知和后台任务对应异步工具结果回传。
- 平台能力差异对应工具能力降级。
面试追问
1. 什么时候跨端框架划算,什么时候应该原生分端?
查看深度解析
标准回答: 多端业务与交互高度相似、团队希望共享迭代且平台专属能力有限时跨端划算;核心体验依赖平台原生组件、重性能、硬件能力或各端产品差异很大时应分端,或采用共享领域层加原生 UI。
原理展开: 评估共享代码比例之外,还要计算适配、调试、发布审核、性能和框架升级成本。跨端不是一次开发零适配。
工程示例: 企业表单和商品列表适合共享;重度相机、实时音视频或复杂原生手势页面可单独原生实现。
常见误区: 用页面数量决定选型,忽略最关键 20% 页面可能占 80% 体验成本。
继续追问: 如何做低风险验证?
回答方向: 选一个包含典型网络、导航和平台 API 的纵向切片 PoC,在目标低端机上测性能与开发周期。
2. 小程序和 H5 在处理 SSE 时有什么限制?
查看深度解析
标准回答: H5 可用 EventSource 或 fetch 流,但受浏览器连接、代理、后台节流和认证方式限制;小程序没有统一完整浏览器 API,不同平台的请求分块、并发、后台生命周期和网关支持不同,必须以目标平台能力验证。
原理展开: SSE 还可能被 CDN/代理缓冲,Cookie 与自定义请求头能力也影响鉴权。跨端层应抽象事件流,而不是假设底层都是 EventSource。
工程示例: H5 adapter 解析 EventSource,某小程序 adapter 使用平台 request 的 chunk 回调;两者都输出统一 AsyncIterable<StreamEvent>。
常见误区: 只在桌面浏览器测试,或把“HTTP 请求成功”当作分块一定实时到达。
继续追问: 平台不支持可靠流怎么办?
回答方向: 回退到短轮询、任务状态查询或 WebSocket;协议层保留事件 ID,体验上明确刷新频率和取消行为。
3. JSBridge 如何保证回调不会丢失?
查看深度解析
标准回答: 每次调用生成唯一 request ID,把 Promise resolver 存入待处理表;原生响应携带同一 ID。调用层还要处理注册完成时机、超时、重复响应、页面销毁和原生进程重建,并保证清理幂等。
原理展开: 桥接本质是跨运行时消息协议,无法只靠一个全局回调名;双方需要版本、schema 和明确的 success/error/cancel 结果。
工程示例: invoke('pickImage') 超过 30 秒自动 reject;页面卸载批量取消 pending,请求结果晚到时按 ID 丢弃并记录诊断。
常见误区: 把 callback 函数直接序列化给原生,或只处理成功路径,导致 pending map 永久增长。
继续追问: App 切后台后原生任务仍完成怎么办?
回答方向: 持久任务使用 task ID 和状态查询;页面恢复后重新订阅或拉取结果,不依赖旧 JavaScript 闭包仍存在。
4. 跨端项目如何做条件编译而不破坏可维护性?
查看深度解析
标准回答: 把条件编译限制在平台入口、适配器和无法被其他平台解析的资源处,业务层依赖统一接口。差异较小可用能力检测,差异较大则拆成同名平台实现文件。
原理展开: 平台分支散落会造成组合爆炸和未测试路径;需要共享契约测试和每个平台的构建矩阵。
工程示例: share/index.ts 按平台导出 share(payload): Promise<Result>,页面只调用该接口;微信和 H5 实现在独立文件中。
常见误区: 在每个组件中写多层平台宏,或条件编译改变领域语义却没有类型体现。
继续追问: 如何防止某个平台分支长期坏掉?
回答方向: CI 至少编译所有平台,关键适配器跑契约测试;发布前在真实宿主做少量端到端冒烟。
实践任务
实现一个 uni-app 或 H5 的 AI 聊天页,处理流式输出、重连和平台能力差异。
验收标准
- 平台差异集中在显式桥接或条件编译处。
- 页面销毁后回调不会继续更新状态。
- 能说明小程序的流式限制。