H5 调试实战

H5 调试实战(二):Android WebView 白屏排查,从 Console 远程定位到 Mock 验证

白屏了

事情发生在一次活动 H5 压测。

测试在群里发了一段录屏:

「三星 S23,Android 14,微信里面打开活动页,点击『立即参与』之后页面白屏。」

比较奇怪的是,这不是完全打不开。

页面能够正常加载:

  • 首页内容正常展示;
  • 按钮可以点击;
  • 点击后有 loading;

但是随后页面直接白屏。

iOS 正常,其他 Android 设备也正常。

这种问题第一反应通常会看几个方向:

  • JavaScript 是否运行异常;
  • 接口返回数据是否导致渲染失败;
  • Android WebView 是否存在兼容问题。

先从网络层开始排查。

抓包正常,但页面还是挂了

先看接口请求。

POST /api/campaign/join

返回:

200 OK

请求参数正常。

响应数据结构也符合预期。

至少可以确定:接口没有直接失败。

但是页面依然白屏。

这类问题比较麻烦:请求成功了,页面也加载了,但某一步执行失败。

如果没有运行时日志,只能靠猜。

vConsole 和 Remote Debug 都不太适合这个场景

通常 H5 页面调试,第一个想到的是 vConsole。

但是这个活动页面已经上线,测试环境无法直接重新打包注入 vConsole。

而且问题只发生在特定 Android 设备。

Chrome Remote Debug 也尝试过:

chrome://inspect

可以连接 WebView。

但是问题发生在点击按钮之后。刷新页面时错误已经不存在。

另外:

  • WebView 调试开关需要客户端支持;
  • USB 连接有环境要求;
  • 线上版本不方便反复调整。

现在的问题是:手机里的微信 WebView 已经出现异常,但我需要看到它真实运行时的状态。

先拿到线上 WebView 的 Console

测试机通过代理连接 DevPeek。

完成:

  • 安装 HTTPS 证书;
  • 配置代理;
  • 开启目标域名 SSL 解密。

完整前置清单见 移动端网页调试;若预览区停在「等待设备连接」,先过一遍 常见问题

然后在 DevPeek 调试 Tab 中选择对应设备,测试重新打开活动页面。

控制台马上出现了一条异常:

Uncaught TypeError:
e.isIntersecting is not a function

at IntersectionObserver polyfill
(chunk-vendors.js:1847)

DevPeek 调试 Console:报错与远程 eval

真机控制台输出实时推送到调试面板。

问题方向开始明确。

原因:polyfill 覆盖了 Android 原生实现

这里涉及 IntersectionObserver

Android 14 的 WebView 已经支持这个 API。

但是项目里仍然加载了一份旧版 polyfill,而这份 polyfill 又覆盖了浏览器原生实现。

于是页面实际运行的不是 native IntersectionObserver,而是 polyfill 版本。两套实现之间存在兼容差异。

继续通过 Console 远程执行几行代码确认:

navigator.userAgent.match(/Android\s([\d.]+)/)?.[1];
// "14"  ——确认 Android 版本

typeof IntersectionObserver;
// "function"  ——确认 API 存在

IntersectionObserver.toString().includes("native code");
// false  ——确认当前实现不是浏览器原生版本

定位完成。

问题定位了,但测试还没结束

前端开始修复 polyfill。

但是压测窗口还在继续。

测试还需要确认:如果接口返回异常状态,页面的降级逻辑是否正常。

例如:

  • 活动已结束;
  • 用户没有权限;
  • 活动库存不足。

这些场景不能每次都等后端配合,所以需要模拟接口响应。

用 Mock 验证异常场景

在 DevPeek 抓包列表找到 POST /api/campaign/join

创建 Mock 规则。

匹配条件:

  • URL 包含 campaign/join
  • Method 为 POST

修改响应:

{
  "code": 10086,
  "message": "活动已结束"
}

保存并启用。

DevPeek Mock 规则编辑:匹配条件与响应篡改

Mock 规则保存即生效,不需要重启代理。

测试重新点击按钮。

这次页面没有白屏,而是正常显示「活动已结束」。

降级逻辑验证通过。

如果继续测试其他错误场景,例如:

{
  "code": 10010,
  "message": "当前活动不可用"
}

可以继续修改 Mock 响应,快速验证不同异常状态下的页面表现。

整个过程不需要:

  • 修改后端接口;
  • 等待接口发布;
  • 重新打包 H5。

Mock 规则是持久化的,关掉 DevPeek 再打开仍然有效。联调结束后记得禁用或删除临时规则,避免影响后续抓包。

整个排查过程

这次的问题最终定位链路:

点击按钮
    ↓
抓包确认接口正常返回 200
    ↓
Console 获取 WebView 运行时异常
    ↓
远程 eval 确认 polyfill 覆盖问题
    ↓
修复方案确认
    ↓
Mock 模拟异常接口
    ↓
验证页面降级逻辑

整个过程中,没有重新打包 H5,也没有要求客户端开启调试模式。

为什么这次排查比较顺

以前遇到类似问题,通常需要几个工具配合:

  • 抓包工具确认请求;
  • vConsole 查看运行日志;
  • 修改接口响应测试异常流程。

每个工具都能解决一部分问题。

但排查过程中经常会遇到:

  • 刚才那个错误日志在哪里?
  • 这个接口刚才返回了什么?
  • Mock 修改之后手机有没有重新触发?

问题信息分散在不同工具里。

这次所有信息都在同一条调试链路中:

Network
   ↓
Console
   ↓
Mock
   ↓
真实设备页面

对于移动端 H5 调试来说,减少工具切换,本身就是减少排查成本。

一些实际限制

当然,Console 和 Mock 也不是万能的。完整边界见 移动端网页调试 文档。

1. 页面已经彻底崩溃

例如无限循环、栈溢出、主线程阻塞——这种情况下调试脚本可能也无法注入。需要先从资源加载和网络请求方向排查。

2. iframe 页面

Console 默认运行在当前页面上下文。如果问题发生在 iframe 内,需要单独打开 iframe 地址调试。

3. Mock 不一定能拦截所有请求

Mock 依赖代理链路。Service Worker 缓存、客户端原生网络层拦截、某些特殊 WebView 内核行为可能绕过。需要确认请求确实经过代理。

4. 远程 eval 的执行环境

远程执行脚本和页面共享上下文。简单检查没有问题,但不要直接执行大量遍历 IndexedDB 或长时间计算任务,建议使用异步方式。

下一篇

《H5 调试实战(三):待整理》——继续记录一些真实移动端 H5 联调案例。


如果你也经常遇到接口正常但是页面异常、Android WebView 兼容问题、线上 H5 无法直接调试,欢迎 下载 DevPeek 试用,或到 GitHub Discussions 聊聊。

相关文档

系列文章