主题
调试与排错
挂件在主播酱里以 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 空白
按以下顺序排查:
- manifest 路径写错:宿主在加载阶段会给出错误提示。打开 DevTools Console,查看是否有
[widget] manifest schema error之类日志,会告诉你具体字段。 <script src="…">404:在挂件 iframe DevTools 的 Network 面板看index.html和zbj-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/下托管。
- SDK 必须从
background没设透明:身 body 默认白色背景会盖住一切。HTML 与 body 都得background: transparent。- 挂件抛了未捕获异常:DevTools Console 会看到红色报错。最常见的是「在
ZBJ.ready之前读了ZBJ.config.xxx」——config在init到达前是空对象{}。
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 礼物 / 弹幕事件收不到
按概率排序:
- 没绑定直播账号:主播酱当前没有连接任何平台时
/ws上没有事件流。在主界面绑定账号、开播。 ZBJ.on("gift", …)拼错事件名:合法值见 平台事件 §1:gift / like / chat / enter / social / subscribe / room。- 平台不支持该事件:例如某些平台没有
social事件。事件矩阵见 events.md 平台事件能力差异。 - 挂件刚拖到画布: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 模式下仍然丢,按以下顺序排查:
- 同一挂件多个实例(
instanceId不同)storage 互相隔离;切回的可能是另一个实例。 - 值不可
JSON.stringify(包含函数 /BigInt/ 循环引用),宿主静默丢弃。 - 挂件被删除并重新添加——
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?