Skip to content

Widget SDK API

Widget SDK 是一个无构建依赖的单文件脚本(zbj-widget-sdk.js),由主播酱内嵌服务托管。挂件页面引入后会在全局 window.ZBJ 上暴露 API,并自动完成初始化与平台事件订阅。作者无需处理底层协议。

1. 引入方式

1.1 已安装挂件(同源托管)

html
<script src="/widget-sdk/zbj-widget-sdk.js"></script>

1.2 开发者模式(挂件运行在自有 dev server,跨源)

html
<script src="http://127.0.0.1:9855/widget-sdk/zbj-widget-sdk.js"></script>

客户端内嵌服务端口默认为 9855。宿主在 iframe URL 的 ?zbjHost= 查询参数中始终透传当前 host,SDK 会自动读取以容纳未来端口变更。

2. 完整类型声明

挂件项目可从 /widget-sdk/zbj-widget-sdk.d.ts 拉取最新声明。下方接口为权威定义。

ts
/* 主播酱创意工坊 · Widget SDK v1 类型声明 */

export type ZBJMode = "edit" | "preview" | "virtualcam";
export type ZBJSurface = "canvas";
export type ZBJPlatform =
  | "bilibili" | "douyin" | "kuaishou" | "shipinhao" | "xiaohongshu";

export interface ZBJSize { width: number; height: number; }

/* 事件载荷见 events.md §1,此处省略具体接口体 */
export interface ZBJBaseEvent { /* ... */ }
export interface ZBJGiftEvent extends ZBJBaseEvent { /* ... */ }
export interface ZBJLikeEvent extends ZBJBaseEvent { /* ... */ }
export interface ZBJChatEvent extends ZBJBaseEvent { /* ... */ }
export interface ZBJEnterEvent extends ZBJBaseEvent { /* ... */ }
export interface ZBJSocialEvent extends ZBJBaseEvent { /* ... */ }
export interface ZBJSubscribeEvent extends ZBJBaseEvent { /* ... */ }
export interface ZBJRoomEvent { /* ... */ }
export type ZBJAnyEvent =
  | ZBJGiftEvent | ZBJLikeEvent | ZBJChatEvent | ZBJEnterEvent
  | ZBJSocialEvent | ZBJSubscribeEvent | ZBJRoomEvent;

export interface ZBJEventMap {
  gift: ZBJGiftEvent;
  like: ZBJLikeEvent;
  chat: ZBJChatEvent;
  enter: ZBJEnterEvent;
  social: ZBJSocialEvent;
  subscribe: ZBJSubscribeEvent;
  room: ZBJRoomEvent;
  message: ZBJAnyEvent;     // 全量原始流
}

export interface ZBJGift {
  id: string | number;
  name: string;
  price: number;
  icon: string;
}

export interface ZBJRoomInfo {
  platform: ZBJPlatform;
  roomId: string;
  memberCount: number;
  totalUserCount: number;
  followCount: number;
}

export interface ZBJSdk {
  /** SDK 主版本号,恒为 1 */
  readonly sdkVersion: 1;

  /* ── 生命周期与上下文 ──────────────────────────────────── */
  /** init 完成后触发;SDK 启动时从 URL hash 同步读出 init,因此通常在同步代码末尾即 ready */
  ready(callback: () => void): void;
  readonly mode: ZBJMode;
  readonly surface: ZBJSurface;
  readonly platform: ZBJPlatform;
  readonly widgetId: string;
  readonly instanceId: string;
  readonly size: Readonly<ZBJSize>;
  onResize(callback: (size: ZBJSize) => void): void;
  /** 请求宿主把元素尺寸改为指定值;宿主可拒绝(超出 min/maxSize) */
  requestResize(width: number, height: number): void;

  /* ── 配置与主题 ───────────────────────────────────────── */
  /** manifest.config 当前值;同时已写入 CSS 变量 --zbj-<key> */
  readonly config: Readonly<Record<string, unknown>>;
  onConfigChange(
    callback: (config: Readonly<Record<string, unknown>>) => void
  ): void;
  /** manifest.theme 当前值;同时已写入 CSS 变量 --zbj-<key> */
  readonly theme: Readonly<Record<string, unknown>>;
  onThemeChange(
    callback: (theme: Readonly<Record<string, unknown>>) => void
  ): void;

  /* ── 实时事件 ─────────────────────────────────────────── */
  on<K extends keyof ZBJEventMap>(
    event: K,
    cb: (e: ZBJEventMap[K]) => void
  ): void;
  off<K extends keyof ZBJEventMap>(
    event: K,
    cb: (e: ZBJEventMap[K]) => void
  ): void;

  /* ── 数据查询 RPC(经宿主中转)─────────────────────────── */
  invoke<T = unknown>(method: string, ...args: unknown[]): Promise<T>;
  /** 当前平台礼物列表;等价于 invoke("getGifts") */
  getGifts(): Promise<ZBJGift[]>;
  /** 当前房间信息快照;等价于 invoke("getRoomInfo") */
  getRoomInfo(): Promise<ZBJRoomInfo>;

  /* ── 实例级持久化(随场景一起保存)──────────────────────── */
  readonly storage: {
    get<T = unknown>(key: string): T | undefined;
    set(key: string, value: unknown): void;
    remove(key: string): void;
    keys(): string[];
  };

  /* ── 工具 ─────────────────────────────────────────────── */
  /** 日志转发到宿主开发者控制台,附带挂件标识前缀 */
  log(...args: unknown[]): void;
}

declare global {
  const ZBJ: ZBJSdk;
  interface Window { ZBJ: ZBJSdk; }
}

3. 行为契约

3.1 生命周期

阶段触发条件
脚本加载iframe 文档解析到 <script src="…/zbj-widget-sdk.js">
initSDK 启动时同步location.hashzbjInit 参数读取宿主预填的 mode/platform/config/theme/widgetState/size/host/wsUrl 等,立即写入 ctx 并应用 CSS 变量。没有握手消息:iframe 加载即 ready
readyinit 写入完成后置 ready=true,microtask 触发所有 ZBJ.ready 注册的回调;之后注册的回调立即补触发一次
WS 连接init 应用后异步打开 wsUrl,仅用于消费平台事件流(与宿主通信无关)
配置变更宿主每次下发 {source:"zbj-host", type:"config"/"theme"} postMessage,SDK 先更新 CSS 变量再触发 onConfigChange / onThemeChange
尺寸变更宿主每次下发 type: "resize" postMessage,SDK 更新内部 size 并触发 onResize

SDK 内所有用户回调由 SDK try/catch 兜底;单个回调抛错经 console.error 上报,不会影响其它回调与后续事件分发。

3.2 模式 mode

取值来源行为
edit主窗口编辑区storage.set 经宿主持久化到 element.widgetState,随场景保存
preview/preview 预览窗storage.set 仅在会话内有效,不落盘
virtualcam虚拟摄像头离屏窗preview

挂件应根据 mode 自行调整行为(如 virtualcam 模式下关闭交互态的视觉提示)。

3.3 配置与主题

  • ZBJ.configZBJ.theme 均为只读快照;变更后会接收到回调,回调参数即为新对象。
  • 每次变更时,SDK 先调用 applyCssVars 写入 CSS 变量(详见 开发约定 §3 CSS 变量桥),再触发回调。

3.4 实时事件 on / off

  • event 必须是 ZBJEventMap 的键。未知键不会报错,永不触发。
  • on("message", cb) 收全量归一化事件流;其他键收对应类型的事件。
  • 平台事件由 SDK 在 iframe 内直连 /ws 消费,详见 通信协议 §4 WebSocket平台事件

3.5 RPC invoke

ts
ZBJ.invoke<T>(method: string, ...args: unknown[]): Promise<T>
条件行为
method 在宿主白名单内宿主代为请求并通过 rpc-result 回传结果
method 不在白名单Promise reject,错误信息 RPC method "<method>" not allowed
10 秒内无响应Promise reject,错误信息 RPC timeout: <method>

当前白名单(实现于 el-custom-widget.vuehandleRpc):

method参数返回
getGiftsZBJGift[],当前平台礼物列表
getRoomInfoZBJRoomInfo,由 status store 与 platform 状态组装

getGifts()getRoomInfo()invoke 的语法糖,签名与类型更友好。

3.6 实例级持久化 storage

操作行为
get(key)读本地缓存(init 下发的 widgetState + 后续 set 的乐观更新)
set(key, value)本地缓存乐观更新;经 postMessage state 通知宿主;宿主在 edit 模式下写入 element.widgetState 并随场景持久化(preview / virtualcam 模式下仅作内存态)
remove(key)set,但操作类型为 remove
keys()返回当前本地缓存的所有键

值必须可 JSON.stringify 序列化。storage 是「实例级」的:同一挂件多次添加到画布产生的多个实例彼此隔离。

3.7 日志 log

ts
ZBJ.log("hello", { foo: 1 });

会被宿主以 [widget] <widgetId> hello { foo: 1 } 的形式输出到主播酱主进程日志。生产环境下挂件页面的 console.log 可能被构建工具移除;通过 ZBJ.log 可在生产构建中保留日志能力。