主线程,只有一条车道
点一下交易按钮,却整整一秒没反应——这不是个别 bug,而是模型的天花板。在浏览器/WebView 里,JS 引擎与渲染共享同一条主线程:脚本执行、布局、绘制、输入响应全挤在一条车道上。一旦有长任务(重计算、大 JSON 解析、框架 hydration、第三方标签),绘制与输入就被堵住。web.dev 对 TBT 的定义很直白:运行超过 50 毫秒的任务即「长任务」,会阻塞主线程;2025 Web Almanac 测得移动端 TBT 中位 1,916 毫秒、第 90 百分位 7,555 毫秒。同一段脚本在笔记本上跑得顺,在中端手机上却能让界面僵住一秒以上。
WebView:一条线程包办一切
浏览器与 WebView 中,JS 引擎与渲染同处一条主线程。一个长任务——重计算、巨型 JSON 解析、框架水合、第三方标签——会同时堵死绘制与输入。用户的点按已经触发,但浏览器必须等任务跑完才能回应。这正是 H5 在移动端的结构性天花板:你自己的逻辑和「让画面动起来」争抢同一条线程。
小程序:逻辑与渲染,各走各的
微信的双线程模型把两者拆开:逻辑层在独立 JS 引擎(iOS 的 JavaScriptCore、Android 的 V8/X5)中运行,没有 DOM;渲染层是一个只负责绘制 WXML/WXSS 的 WebView。两者不能直接共享内存,只通过 Native 桥以 setData 通信——数据被序列化、diff、再套用到视图。于是,逻辑层里的重计算不会冻结渲染层的绘制;渲染层因为自身工作很轻,能持续回应用户的触摸。
(诚实地说:setData 本身有成本——序列化 + diff + 跨线程传输。过大或过频的 setData 也会带来自己的卡顿。所以真正的收益是「计算不再阻塞绘制」,但你得让 setData 保持精简。微信的 Skyline 是更新的渲染引擎,进一步贴近原生。)
流畅从哪来
- 关注点分离:重计算与绘制隔离,互不等候。
- 渲染线程轻量:它只做 diff 与绘制,不跑业务逻辑。
- Native 中介:桥负责批量转发,渲染层不必等你的循环。
- 移动端可预测:困扰 H5 中端机的卡顿,在结构上被削弱。
逐项对比
| 维度 | WebView / H5 | 小程序 |
|---|---|---|
| 线程模型 | 单一主线程(JS+布局+绘制+输入) | 双线程:逻辑引擎 + 渲染 WebView |
| 长任务影响 | 阻塞绘制与输入(点按冻结) | 计算隔离,渲染持续绘制 |
| 移动端 TBT(中位) | 1,916 ms;p90 7,555 ms | 渲染线程不占 JS 关键路径 |
| 逻辑层有 DOM? | 有(可直接阻塞) | 无,仅经 setData 更新 |
| 需盯的成本 | 无上限的 JS | setData 的体积/频率 |
| 最适合 | 开放网络内容 | 宿主内交易型 UI |
对你的流程意味着什么
对交易界面、聊天、表单来说,响应速度就是产品本身。把交易核心搬进小程序,是从结构上移除 H5 在中端机上撞上的「一条线程包办一切」天花板。H5 适合开放网络;宿主内的核心,不该和自己的重逻辑共用一条主线程。
Cross Mini App 的位置
Cross Mini App 是双线程模型下的 runtime 与开放 catalog。你构建一次,便在任一宿主里以受管桥把逻辑与渲染分离运行,于是重计算永远抢不走那帧对点按的回应。H5 仍是门,小程序则是那个按钮永远应答的房间。