接口 200 但页面白屏?Android WebView IntersectionObserver polyfill 排查实录

接口 200 但页面白屏?Android WebView IntersectionObserver polyfill 排查实录
活动 H5 在部分 Android 设备上点击按钮后白屏抓包却显示接口全部 200——这类问题网络层给不出答案得看 JavaScript 运行时。下文按「抓包 → Console → Mock」的顺序记录一次完整排查。现象能进页、能点按钮随后白屏压测群里发来录屏「三星 S23Android 14微信里面打开活动页点击『立即参与』之后页面白屏。」不是整页打不开。首页正常、按钮可点、点击后有 loading随后整页变白。iOS 和其他 Android 机型均正常。第一反应通常会拆三条线JS 运行时是否在交互后抛错、中断渲染接口数据是否触发了前端未处理的边界WebView 内核是否存在机型相关的 API 兼容差异。先从最容易证伪的网络层下手。第一步抓包排除「接口挂了」手机配置 HTTPS 代理Charles、mitmproxy 或同类工具均可安装并信任根证书对目标域名开启 SSL 解密。在活动页点击「立即参与」找到POST /api/campaign/join响应200 OKBody 结构与文档一致业务字段无异常。结论网络层正常白屏是前端执行链路在某一步断掉。没有运行时日志的话只能对着 200 瞎猜——是渲染逻辑 bug数据结构踩坑还是某个 Web API 在特定内核上炸了第二步为什么 vConsole 和 chrome://inspect 都不好使vConsole活动页已上线测试包没法随时重打注入 vConsole。即便能注入也得先知道「点按钮之后才崩」——错误若只在交互瞬间出现日志容易被后续路由跳转冲掉。Chrome Remote Debugchrome://inspect → 选中 WebView → Inspect能连上但有两个硬伤复现窗口短问题出在点击之后一刷新错误栈消失得反复操作才能撞上。环境门槛WebView 需开setWebContentsDebuggingEnabled、USB 调试、对应内核支持——线上包、微信内置 WebView 经常凑不齐。需要的是不改编译产物、不依赖 USB在异常发生的当下拿到 Console 输出。第三步代理注入拿到 WebView 运行时 Console思路是走代理链路向 HTML 响应里注入一段调试 bootstrap页面加载后在真机上下文里挂 Console 钩子日志回传到电脑端面板——类似 Eruda / vConsole但不用改 H5 构建线上 URL 直接可用。前置步骤手机 Wi‑Fi 代理指向抓包工具安装并信任 HTTPS 根证书对活动域名开启 SSL 解密打开调试面板等设备连上后复现「点击 → 白屏」。笔者这次用的是 DevPeek 的代理注入调试Charles 自写脚本、mitmproxy addon 也能做同类注入核心都是「代理改写 HTML 回传 Console」。复现后Console 立刻出现Uncaught TypeError: e.isIntersecting is not a function at IntersectionObserver polyfill (chunk-vendors.js:1847)栈指向IntersectionObserver的 polyfill而不是业务组件——方向明确了。根因旧 polyfill 覆盖了 Android 14 的原生实现IntersectionObserver用于监听元素是否进入视口懒加载、曝光埋点、无限滚动列表都常见。Android 14 系统 WebViewChrome 108已原生支持该 API。但项目打包时仍无条件引入了旧版intersection-observerpolyfill且在初始化时覆写了window.IntersectionObserver。旧 polyfill 的回调参数结构与原生不一致原生IntersectionObserverEntry上isIntersecting是 boolean旧 polyfill 部分版本用不同字段或省略该 getter。业务代码或依赖库按原生写法访问entry.isIntersecting在 polyfill 路径下就变成undefined is not a function一类错误后续渲染中断 → 白屏。在 Console 里远程执行几行即可确认// 1. 确认 Android 版本navigator.userAgent.match(/Android\s([\d.])/)?.[1];// → 14// 2. API「存在」但是否为原生typeofIntersectionObserver;// → functionIntersectionObserver.toString().includes(native code);// → false ← 关键跑的是 polyfill不是浏览器实现再对照 Chrome 版本WebView 与系统 Chrome 同源navigator.userAgent.match(/Chrome\/([\d.])/)?.[1];// Android 14 通常 ≥ 108IntersectionObserver 已内置定位完成不是接口、不是业务数据结构是「不该加载的 polyfill 在真机上覆盖了原生 API」。修复方向原则只在缺失时加载禁止无脑覆盖。// ✅ 推荐feature detect不覆盖已有原生实现if(!(IntersectionObserverinwindow)){awaitimport(intersection-observer);}若历史包已全量引入可改为按 UA / Chrome 版本条件加载或在入口显式检测consthasNativeIOIntersectionObserverinwindowIntersectionObserver.toString().includes(native code);if(!hasNativeIO){awaitimport(intersection-observer);}打包侧检查babel-preset-env/vitejs/plugin-legacy是否把IntersectionObserver误判为需 polyfill——Android 10 WebView 多数场景已不需要。第四步Mock 接口验证降级 UIpolyfill 修复进 PR 的同时压测还得继续。测试要确认接口返回业务错误码时页面是否正常降级而不是再次白屏。典型场景场景期望 UI活动已结束提示文案可返回无参与权限权限说明库存不足售罄提示每次都找后端改数据或发预发不现实在代理层 Mock 响应是最快的。在抓包列表找到POST /api/campaign/join配置 Mock 规则Charles 用 Map Local / Rewritemitmproxy 用flow.response脚本逻辑相同匹配URL 含campaign/joinMethodPOST响应体改为{code:10086,message:活动已结束}HTTP 状态码保持200与真实接口一致只改业务 body。保存启用后测试机重新点击。页面未白屏正常展示「活动已结束」——降级逻辑 OK。临时验证也可用「响应拦截改包」请求发出后、到达页面前改写 body改完放行继续换错误码即可批量验 UI例如{code:10010,message:当前活动不可用}全程无需改后端、等发布、重打 H5 包。联调结束记得关闭或删除 Mock 规则避免污染后续抓包。排查链路小结用户点击「立即参与」 ↓ 抓包POST /api/campaign/join → 200body 正常 ↓ ConsoleTypeError: e.isIntersecting is not a function ↓ evalIntersectionObserver 非 native → polyfill 覆盖 ↓ 修复条件加载 / 移除多余 polyfill ↓ Mockcode 10086 / 10010 … → 验证降级 UI未重打包、未开 USB 调试、未改线上包。把 Network 和 Console 放同一时间轴这类问题常卡在「工具切换」抓包工具里看到 200vConsole 里看到报错Mock 改完不确定手机有没有打到新响应。若 Console、Network 与抓包列表同源可以在报错的时间点附近直接点开对应请求对照响应 body「接口 200 返回了什么」和「同一时刻 Console 报了什么」放在一条时间线上比三个 App 来回切省很多时间。局限代理调试通用1. 主线程已死无限循环、栈溢出、严重阻塞时注入脚本可能也跑不起来先从静态资源加载、首屏请求查起。2. iframe 隔离Console 默认在当前 frame。问题在 iframe 内时需对 iframe URL 单独开调试会话。3. Mock 走代理Service Worker 缓存命中、App 原生网络层不走系统代理、部分 WebView 特殊通道可能绕过 Mock。改完包后看抓包列表里是否出现预期请求。4. 远程 eval 别乱来与页面共享上下文适合typeof、toString这类探测避免长时间同步循环或大量 IndexedDB 遍历。总结现象根因手段点击后白屏接口 200旧 polyfill 覆盖原生IntersectionObserverisIntersecting访问失败代理注入 Console 远程 eval需验多种错误码 UI后端不便配合代理 Mock 响应 bodyTakeaway「接口 200」只说明 HTTP 层没问题WebView 白屏优先查JS 运行时异常机型相关的 polyfill 误覆盖是高频坑。Android 10 部署前建议对关键 Web API 做一次fn.toString().includes(native code)抽检。文中截图来自 DevPeekCharles、mitmproxy 等工具在抓包与 Mock 环节可平替差异主要在是否自带远程 Console 注入。若你常做微信 / WebView 联调把「抓包 运行时日志 Mock」串成一条链路比单点工具省不少来回。