多实例示例

← 单实例示例 | 核心 API →

多实例演示使用独立 Subframe:父页面负责 Tab 和工具栏,每个 Tab 的 OnlyOffice 运行时都放在一个独立子域名页面中。

a.localhost
├── a.b.localhost/subframe  (Word)
├── b.b.localhost/subframe  (Excel)
└── c.b.localhost/subframe  (PPT)

每个子框架的响应都带有 Origin-Agent-Cluster: ?1。因此 SDK 的全局对象、事件总线、socket mock 和文档状态不会在多实例之间共享;父页只通过带 source、实例 ID 和精确目标 origin 校验的 postMessage RPC 控制子框架。

在线演示:站点路由 /docs/demos/multi

父页面源码src/features/demo/tabs-multi-page.tsx

子框架页面src/features/demo/onlyoffice-subframe-page.tsx 消息协议src/features/demo/subframe-protocol.ts

在线演示

下方为可交互的多实例 Tab 编辑器。

示例

切换标签页,实例状态会保留

更多
加载中...

设计要点

要点说明
独立 origin第一个 Tab 使用 a.b.localhost,后续 Tab 依次使用 b.b.localhostc.b.localhost
独立运行时每个 /subframe 页面拥有自己的 OnlyOfficeManager、SDK 和事件总线
懒加载Tab 首次激活时,父页通过 RPC 发送 open 请求
状态保留非激活 Tab 只隐藏外层 iframe,不卸载子框架
跨窗口控制上传、主题、只读、导出、批注、修订和 Connector 都通过 RPC 转发
关闭 Tab父页发送 destroy,再移除对应 iframe
资源切换更新 iframe 的资源参数并重新加载各子框架
响应头TanStack Start 的 Cloudflare Worker 全局响应头设置 Origin-Agent-Cluster: ?1

Tab 数据模型

type TabItem = {
  id: string;
  label: string;
  containerId: string;
  subframeHost: string;
  fileName: string;
  readOnly: boolean;
  docKind: "word" | "excel" | "ppt";
};

子框架 URL

const url = new URL("/subframe", getSubframeOrigin(tab.subframeHost));
url.searchParams.set("instance", tab.id);
url.searchParams.set("resourceMode", resourceState.mode);

getSubframeOrigin("a") 在本地开发时返回 http://a.b.localhost:<port>;生产环境会根据当前站点域名生成对应的 a.b.<domain> 子域名。部署时需要让这些子域名解析到同一个应用服务。

RPC 生命周期

iframe load → subframe:ready
parent      → request/open
subframe    → event/loading-change + event/document-ready
subframe    → response/open
parent      → request/set-theme | request/set-read-only | request/editor

子框架不会把 SDK 原始对象传回父页:批注和修订会先转换为可结构化克隆的数据,Connector 命令则只传递函数源码字符串和调用结果。