前言
阿里内部使用的 windvane 容器原理剖析
抛出问题
- 端上设计缺陷问题处理(iOS首页白屏,Bridge 调用卡顿)
- 统一两端方案
- 安全加固
- 性能优化(加载速度过慢)
- 双端感知(H5 内部跳转(非原生跳转) 情况下 端上如何感知)
- H5 沉浸式页面(如全屏视频、游戏、直播)
解决!
时间线
---------------------------------------------------------
| WindVane架构演进 |
---------------------------------------------------------
| 年份/阶段 | 2013-2014 | 2015 | now |
---------------------------------------------------------
| 交互时 | Hybrid API| | |
| | 平台化 | | |
---------------------------------------------------------
| 下载时 | | 预加载 | |
| | | HTTP-DNS | |
| | | SPDY | |
---------------------------------------------------------
| 渲染时 | | | 渲染 |
---------------------------------------------------------前期主要处理交互问题,后期开始处理下载问题以及渲染问题。
首页白屏问题
分两步
- app 第一次进入 wk 会白屏,解决方案:适当时机(比如进入骑手个人中心页面后)就启动一个空白 wkwebview。
统一两端方案
这个不用说,web 负责渲染 UI,逻辑部分通过 Javascript 各自端上实现。我们分开来看
- web 页面渲染只需要 Html + CSS(当然,部分 UI 展示需要借助一定的 JS 逻辑)
- Javascript 通过 Web 容器提供的通道来实现双向通信
安全加固
三个方案:
- 参数校验(Schema Validation)
参数不对则拒绝 - 来源校验(Origin Check)
不是 饿了么 host, 则拒绝 - 防重放攻击
每次 invoke 带唯一 nonce + 时间戳;服务端校验防止重复提交。
性能优化(加载速度过慢)
离线包。静态资源(js、html、css)本地加载即可。
亮点
WindVane 采用 “预注入 + 动态增强” 双阶段注入策略,在页面加载前完成基础能力注入,运行时按需注入业务 SDK。
流程图:
阶段一:预注入 核心 js
时机:WKWebView 初始化后、loadRequest 之前
注入的内容:
- window.WindVane
- Promise 风格的 invoke(method, params) 方法
- __callbacks 映射表
- 基础事件监听(如 onPageReady)
注意
为什么必须在 loadRequest 前注入?
确保 H5 页面第一行 JS 就能调用 Bridge,避免“Bridge not ready”错误。
阶段 2:动态注入业务 SDK(按需)
- 时机:
收到 H5 的特定信号(如 <meta name="windvane-modules" content="login,tracker">) - 方式:
从本地缓存或 CDN 下载 login_sdk.js
通过 evaluateJavaScript 注入
- 优势:
减少首屏 JS 体积
按业务域隔离能力(防滥用)
数据:淘宝早期因 Bridge 未就绪导致的 JS 错误率 >15%,注入前置后降至 <0.1%。
保证跨平台一致性:前端无需写 if (isIOS) ... else if (isAndroid) ...。
双端感知
监听 History API:
WindVane 会注入 JS 代码,劫持 pushState / replaceState:
const originalPush = history.pushState;
history.pushState = function() {
originalPush.apply(history, arguments);
// 通知原生:页面已跳转
windvane.notifyPageChange(location.href);
};H5 沉浸式页面
什么是“沉浸式”?
- 页面需要 隐藏状态栏、导航栏
- 可能要求 横屏锁定
- 用户手势(如下滑)可能触发 退出全屏 而非返回上一页
- 原生 UI(如 ActionBar)需动态隐藏/显示
WindVane 如何处理?
扩展 JSBridge API
提供类似:
// H5 主动请求沉浸式
windvane.setImmersiveMode({
immersive: true,
orientation: 'landscape',
hideNavBar: true
});原生容器收到后,动态调整 Activity 的 UI 和屏幕方向。
手势代理机制
WindVane 监听 WebView 的 touch 事件
若 H5 声明“当前处于沉浸式”,则拦截系统返回手势,交由 H5 处理(如退出全屏)
通过 shouldOverrideKeyEvent 或自定义 GestureDetector 实现
生命周期透传
当 App 进入后台(onPause),WindVane 主动调用 H5 注册的 onAppBackground() 回调
H5 可在此暂停视频、释放资源
权限预声明
在离线包配置中声明“该页面需要沉浸式权限”
WindVane 加载前检查,避免运行时失败
虚拟页面栈管理
WindVane 维护一个 H5 内部页面栈(独立于原生 Activity 栈)
每次 notifyPageChange 就 push 一个新“虚拟页面”
按返回键时,先 pop 虚拟栈;栈空才关闭 WebView
动态埋点上报
收到 notifyPageChange 后,自动触发新页面的 PV 上报
性能指标(FCP、LCP)按虚拟页面重置
总结
+---------------------+--------------------------------------+
| 问题 | WindVane 方案 |
+---------------------+--------------------------------------+
| 首屏白屏 | 预注入 + 离线包 → H5秒开 |
| Bridge 调用卡顿 | 异步队列 + 批量合并消息 |
| 内存泄漏 | 自动清理 callback + WebView 复用池 |
| JS 执行阻塞 UI | 所有 evaluateJavaScript 放子线程调度 |
+---------------------+--------------------------------------+具体的阿里内部有在大会分享过:活动家(https://www.huodongjia.com/)