跳转到正文
返回门户

调试方法论:world / 验证 / SPA 选择器 ​

调试注入类应用时最容易踩的三个坑:搞错脚本运行的 world、用不可靠的方式「验证」交互效果、依赖 SPA 里不稳定的选择器。本节给出一套可复用的方法论。

World 语义:脚本到底运行在哪 ​

运行方式World能看到什么
已安装应用 / liveDebug onUSER_SCRIPT(隔离 world)共享页面 DOM,但不共享 JS 全局:看不到页面框架的 window 变量,页面也看不到你的
run --tab <target>(默认)MAIN(页面 world)与页面 JS 同一环境;看不到 USER_SCRIPT world 里 liveDebug 应用的全局状态
run --tab <target> --world user-scriptUSER_SCRIPT(与 liveDebug 同 world)可直接探测 liveDebug 应用的全局变量、监听器状态
LFRuntime.runInMainWorld(fn)从 USER_SCRIPT 跨到 MAIN在应用内读取页面框架数据(如 React/Vue 内部状态)

最常见的误判

用 run --tab 探测「我的应用有没有挂上」,结果 window.__myPanel === undefined,于是断定注入失败——其实脚本好好地跑在 USER_SCRIPT world 里,只是你在 MAIN world 看不到它。探测 liveDebug 应用状态必须加 --world user-script。

两个 world 共享同一个 DOM:DOM 节点、DOM 事件(dispatchEvent 也算)跨 world 可见。不共享的只有 JS 全局对象、原型链补丁和闭包。

验证方法论:看 → 操作 → 断言 ​

  1. 看:先拍快照再动手

    用 snapshotText --tab <t> 获取带 @N 引用的可访问性快照,确认目标元素真实存在、角色和文案符合预期。不要凭记忆写选择器。

  2. 操作:用受信输入而非合成事件

    input hover/click/fill --ref @N 走 CDP 发出受信 (trusted) 输入:能触发 CSS :hover、框架的原生事件委托和浏览器默认行为。dispatchEvent(new MouseEvent(...)) 是合成事件:isTrusted: false,不会触发 :hover 样式,部分框架会忽略。验证 hover 出现的按钮时必须用 input hover。

  3. 断言:用输出而不是「没报错」

    操作后再次 snapshotText(或 input ... --expect ...)确认 UI 达到预期状态。「命令执行成功」不等于「页面发生了预期变化」。

假设分诊:当行为不符合预期时,先用一条最小探测命令区分「代码没注入」「注入了但没挂载」「挂载了但选择器没命中」「命中了但事件没触发」四类原因,再改代码。每轮只验证一个假设。

SPA 选择器指引 ​

  • class 名多为构建期哈希(如 feed-active-video-xK3f),版本更新即失效;优先用语义属性(role / aria-* / data-*)、稳定的文案、或 snapshotText 的 @N 引用定位
  • 同一列表在不同状态下 class 可能不同(如激活项 feed-active-video vs 普通项 feed-video),写选择器前先在多个状态下各拍一次快照对比
  • SPA 路由切换(history.pushState)不更换 document:liveDebug 不会重注入,你的 MutationObserver / 事件委托要能覆盖新渲染的节点;用 LFDOM.waitForElement / LFDOM.locator(自动等待重试)而不是一次性 querySelector
  • 虚拟列表只渲染可视区节点,querySelectorAll 数量 ≠ 数据总量;用 LFDOM.findInVirtualList 收集
  • 悬浮出现的控件(hover 菜单、tooltip)在快照里可能不存在:先 input hover --ref 再拍第二次快照