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 或长时间计算任务,建议使用异步方式。


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

相关文档

系列文章