真机调试实战中那些让你抓狂的坑和解决方案

Good“一茹 移动 阅读 2,519
赞 13 收藏
二维码
手机扫码查看
反馈

真机调试,我到底该信谁?

写这篇文章之前,刚在地铁上用 iPhone 连着 Mac 调试一个 Vue H5 页面,结果 Safari Web Inspector 卡死三次,最后靠 console.log + alert 临时救场——不是我不想用高级工具,是它们真不一定扛得住我写的那些带 MutationObserver + IntersectionObserver + TouchAction 混合操作的破代码。

真机调试实战中那些让你抓狂的坑和解决方案

所以这次我决定不讲虚的,就聊真机调试这事儿:Chrome DevTools(Android)、Safari Web Inspector(iOS)、vConsole、以及我自己最近用得多的「本地代理 + 自建日志面板」方案。不画表格,不打分,就按我踩过的坑、改过的 bug、熬过的夜来排序。

谁更灵活?谁更省事?

先说结论:iOS 上我基本只用 Safari Web Inspector,但必须配一个技巧;Android 我现在几乎不用 Chrome 的远程调试了,转投 vConsole + 本地代理组合;而所有跨端预发环境,我都强制加一层 window.__DEBUG__ = true 控制台开关——因为线上用户报“白屏”,你总不能让他开 Safari 开发者模式吧。

下面挨个说。

Safari Web Inspector:强大但娇气

我承认,它是目前 iOS 真机调试里最接近“原生体验”的方案。能断点、看 Network、查 Storage、甚至模拟地理位置。但前提是:

  • 你的 Mac 和 iPhone 必须登录同一个 Apple ID(这点我忘了十几次)
  • iOS 设置 → Safari → 高级 → 开启「Web 检查器」(别笑,真有人没开)
  • Mac Safari → 偏好设置 → 高级 → 勾选「在菜单栏中显示开发菜单」
  • 然后右键页面 → 「检查元素」——但等等,如果页面用了 document.write()<base href>,它可能直接不显示 Sources 标签页,你得刷新三次再点

另外有个真实坑:iOS 17.4+ 对 fetch 的 CORS 错误提示极其模糊,Network 里看着请求发出去了,Response 却是空的。这时候你得切到 Console,找 TypeError: Load failed,而不是盯着 Network 发呆。

代码上倒是简单:

// 开发环境才暴露 window.debug
if (process.env.NODE_ENV === 'development') {
  window.debug = {
    log: console.log.bind(console),
    error: console.error.bind(console),
  }
}

Chrome DevTools(Android):稳定但慢

曾经是我主力,直到某次调试一个滚动穿透的 Dialog,发现 Chrome 远程调试下 touchstart 事件居然比真机晚触发 300ms——后来查了一圈,是 Chrome 的 Remote Debugging 协议在事件转发时加了缓冲层,尤其在低端 Android 上更明显。

而且它不支持实时修改 CSS 变量(比如 --theme-color),改完要手动刷新;也不支持编辑 <template> 内容,你得切回 Sources 手动改 HTML 文件再 reload,等于回到了 2012 年。

不过它有个优点:Network 面板里能直接 copy as fetch,粘贴就能复现问题,这点比 Safari 强不少。

vConsole:我的兜底方案

我现在所有 H5 项目,只要不是纯内网环境,都会在入口加一段:

<script src="https://unpkg.com/vconsole@latest/dist/vconsole.min.js"></script>
<script>
  if (location.search.includes('debug') || localStorage.getItem('enable_vconsole')) {
    const vConsole = new window.VConsole();
    console.log('vConsole 已启动');
  }
</script>

vConsole 不是万能的,但它够糙、够快、够稳。Network 面板虽然不能像 Chrome 那样看 WebSocket 帧,但至少能看到 fetch 请求头和响应体;Storage 面板能直接编辑 localStorage;Elements 面板支持点击高亮 DOM(这点比 Safari 移动端强太多)。

缺点也很明显:不能断点,不能 step into;如果你用的是 Vue 3 的 <script setup>,vConsole 里看不到响应式依赖链;另外它默认会劫持 console.error,某些异常堆栈会被截断,需要手动 patch:

const originalError = console.error;
console.error = function(...args) {
  originalError.apply(console, args);
  // 同时发到自己的错误收集服务
  fetch('https://jztheme.com/api/log', {
    method: 'POST',
    body: JSON.stringify({ type: 'error', args }),
  });
};

本地代理 + 自建日志面板:我最近的新宠

去年做了一个小程序转 H5 的项目,用户反馈“点按钮没反应”,但真机上又复现不了。后来我在本地搭了个轻量代理(用 Node + http-proxy-middleware),所有 fetchXMLHttpRequest 都过一遍,把 request/response 日志推到一个 WebSocket 面板里,跑在另一个浏览器窗口。

好处是:完全脱离设备限制,iPhone、安卓、甚至微信内置浏览器都能看到实时请求流;还能加 filter(比如只看 /api/order 相关的);配合 sourcemap,甚至能把压缩后的错误堆栈还原成原始行号。

核心逻辑就几行:

// proxy.js
const proxy = require('http-proxy-middleware');

module.exports = function(app) {
  app.use('/debug-log', proxy({
    target: 'http://localhost:3001', // 本地日志服务
    changeOrigin: true,
  }));
};

前端加个 hook:

const originalFetch = window.fetch;
window.fetch = function(url, options) {
  return originalFetch(url, options)
    .then(res => {
      // 记录响应
      fetch('/debug-log', {
        method: 'POST',
        body: JSON.stringify({ url, status: res.status, time: Date.now() }),
      });
      return res;
    });
};

我的选型逻辑

总结一下我现在的日常搭配:

  • iOS 真机初筛:Safari Web Inspector(必须开开发者菜单 + 切到最新版 macOS)
  • Android / 微信 / QQ 浏览器:vConsole + ?debug 参数开关
  • 复杂交互或偶发问题:本地代理 + 日志面板(尤其适合团队协作排查)
  • 绝对不用:Weinre(已废弃)、Eruda(太重,加载慢)、任何需要扫码进调试页的方案(用户不会扫)

最后补一句实在话:没有哪个方案能 100% 覆盖所有场景。上周我还遇到一个 iOS WKWebView 下 input[type=number] 失焦后键盘不收的问题,Safari Inspector 查不到,vConsole 不触发,最后是靠在 blur 里加 setTimeout(() => { document.activeElement?.blur() }, 100) 解决的——有时候,真机调试的本质不是看工具多炫,而是你能多快接受“这里就得硬加一行 hack”。

以上是我踩坑后的总结,有更优的实现方式欢迎评论区交流。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论