Skip to content

调试与排错

挂件在主播酱里以 iframe 形式渲染,调试方式与普通网页大致相同,但有几个挂件特有的角度。本章给出怎么打开调试器怎么看日志常见错误的根因与定位方法

1. 打开挂件 DevTools

主播酱主窗口本身是 Electron 渲染进程;挂件 iframe 是它的子文档。

主播酱模式打开方式
开发版(pnpm dev 启动)主菜单 / 快捷键 Ctrl+Shift+I(或 macOS Cmd+Opt+I)打开主窗口 DevTools,在 Elements 找到挂件 iframe,右键 → Reveal in Sources panel 或在 Console 顶部下拉切换上下文为该 iframe
安装版(生产构建)默认关闭 DevTools(disable-devtool)。开发者模式下注册的挂件仍然可以通过 iframe 内右键 → 检查元素打开 DevTools(仅对开发者注册的源 URL 生效)

在 Console 顶部的 JavaScript context 下拉里选中你的挂件 iframe(条目名类似 Widget: gift-roll),之后所有 console.log、调试断点、网络请求都局限在 iframe 范围内。

2. 看日志

挂件内有两种打印方式,行为不同:

写法输出位置生产构建
console.log(…)挂件 iframe 的 DevTools Console可能被 terser 的 pure_funcs 去除(取决于挂件构建配置)
ZBJ.log(…)主播酱主进程开发者控制台 + 主进程日志文件保留

排查生产环境问题时优先用 ZBJ.log。日志文件位置 Windows 为 %APPDATA%\主播酱\logs\,macOS 为 ~/Library/Logs/主播酱/

每条 ZBJ.log 输出会自动带前缀 [widget] <widgetId> <instanceId>,便于在多挂件场景下定位。

3. 常见问题排查

3.1 挂件白屏 / iframe 空白

按以下顺序排查:

  1. manifest 路径写错:宿主在加载阶段会给出错误提示。打开 DevTools Console,查看是否有 [widget] manifest schema error 之类日志,会告诉你具体字段。
  2. <script src="…"> 404:在挂件 iframe DevTools 的 Network 面板看 index.htmlzbj-widget-sdk.js 是否都是 200。
    • SDK 必须从 http://127.0.0.1:9855/widget-sdk/zbj-widget-sdk.js 加载(开发者模式跨源场景)或 /widget-sdk/zbj-widget-sdk.js(已安装挂件,同源)。
    • 主播酱不通过 file:// 协议加载挂件 —— Chromium 把 file:// 视为 unique opaque origin,会被 CSP / 同源策略 / WebSocket 全面拦截。本机挂件由内嵌 Fastify 在 /widgets//dev-widgets/ 下托管。
  3. background 没设透明:身 body 默认白色背景会盖住一切。HTML 与 body 都得 background: transparent
  4. 挂件抛了未捕获异常:DevTools Console 会看到红色报错。最常见的是「在 ZBJ.ready 之前读了 ZBJ.config.xxx」——configinit 到达前是空对象 {}

3.2 SDK 未加载(ZBJ is not defined

只可能两种:

  • SDK <script> 路径写错或 404;
  • <script> 在使用 ZBJ 的代码之后才加载(顺序错了)。

修复:把 SDK 的 <script> 放在 </body> 前的第一个,再放业务脚本。

3.3 ZBJ.ready 不触发

init 数据通过 iframe URL 的 #zbjInit=<encoded JSON> hash 同步带过来;SDK 启动时 parseInitFromHash() 同步读出,立即 ready=true。若 ready 不触发,可能:

  • 挂件 URL 错误:iframe 加载的不是宿主构造的 URL(比如直接在浏览器打开 index.html 调试),没有 #zbjInit= hash → SDK 无 init 数据 → 不进 ready 分支;
  • URL 被中间层重写:某些代理 / CDN 会丢弃 fragment(hash),但本地 Fastify 不会;
  • ZBJ.ready(cb) 在 SDK 启动之前就调用了(脚本顺序错)—— ready 回调入队,等 SDK IIFE 跑完才补触发,看到的不是真的"没触发"而是延迟几毫秒。

3.4 配置改了,挂件没变

现象原因
调字段值,挂件没反应没注册 onConfigChange,挂件只在 ready 时读了一次 ZBJ.config
注册了 onConfigChange,回调没进检查回调函数是否在 ZBJ.ready 内注册——ready 外注册也行,但要确认时机
主题颜色改了 UI 不变没用 var(--zbj-<key>),而是 JS 里读了一次 ZBJ.theme.xxx 写死。改用 CSS 变量

3.5 礼物 / 弹幕事件收不到

按概率排序:

  1. 没绑定直播账号:主播酱当前没有连接任何平台时 /ws 上没有事件流。在主界面绑定账号、开播。
  2. ZBJ.on("gift", …) 拼错事件名:合法值见 平台事件 §1gift / like / chat / enter / social / subscribe / room
  3. 平台不支持该事件:例如某些平台没有 social 事件。事件矩阵见 events.md 平台事件能力差异
  4. 挂件刚拖到画布:SDK 连 WS 需要数百毫秒,过早开播的事件会丢。后续事件正常。

3.6 RPC 报「method not allowed」

错误信息形如 RPC method "xxx" not allowed

当前 RPC 白名单只有 getGifts / getRoomInfo(见 SDK §3.5)。其他方法都会被宿主拒绝。如需新方法,先看是否能用 ZBJ.on(event) 替代。

3.7 RPC 超时

RPC timeout: <method> 表示 10 秒内没收到响应。原因:

  • 主播酱当前未绑定平台(getGifts / getRoomInfo 拿不到上下文);
  • 网络请求挂起(如礼物列表接口失败)。

3.8 storage 设了下次打开还是空

仅在 mode === "edit"set 才落盘。在预览窗(/preview 路由)和虚拟摄像头窗内 set 仅在会话内有效——这是设计而非 bug,详见 开发约定 §8

如果在 edit 模式下仍然丢,按以下顺序排查:

  1. 同一挂件多个实例(instanceId 不同)storage 互相隔离;切回的可能是另一个实例。
  2. 值不可 JSON.stringify(包含函数 / BigInt / 循环引用),宿主静默丢弃。
  3. 挂件被删除并重新添加——instanceId 重生成,旧数据找不回。

4. 让 iframe 反复刷新而不重启主播酱

在主播酱的「开发者」Tab 已注册的挂件,三种重载方式:

触发条件行为
index.html / app.js 等源码开发者模式下 iframe 监听文件改动自动刷新;如果挂件源 URL 指向了 Vite dev server,则由 Vite HMR 接管,更快
manifest.json客户端监听文件变化,自动重新读取 schema 并刷新元素配置面板;iframe 也会重载一次(config 默认值可能变了)
想手动强制刷删除画布上的挂件元素,再从「我的挂件」拖一次。或在 iframe DevTools 里 location.reload()

5. 排查清单速查

按问题分类,按顺序检查:

白屏 / iframe 空
  □ DevTools Console 有错?
  □ Network 里 index.html 是 200?
  □ Network 里 zbj-widget-sdk.js 是 200?
  □ html/body 都设了 background: transparent?
  □ 业务代码包在 ZBJ.ready 内?

SDK 不响应
  □ <script src="…/zbj-widget-sdk.js"> 在业务脚本之前?
  □ iframe 真的是宿主创建的(不是直接浏览器打开 file://)?
  □ ZBJ.ready 注册了?

配置 / 主题不同步
  □ 注册了 onConfigChange / onThemeChange?
  □ 用 CSS 变量 var(--zbj-xxx) 而不是 JS 里读一次?

事件收不到
  □ 主播酱当前有绑定直播账号且在开播?
  □ 事件名拼写正确(gift/like/chat/...)?
  □ 当前平台支持该事件类型?

RPC 异常
  □ method 名在白名单内(getGifts / getRoomInfo)?
  □ 主播酱有平台上下文?

storage 不持久
  □ mode === "edit"?
  □ 值可 JSON 序列化?
  □ 操作的是同一个 instanceId?