纯前端 OnlyOffice 集成方案(v9)
v7 跑通了,v9 解决了工程化落地难题。
上一篇里,我们用 v7 跑通了「纯前端 Office」的核心链路——基于 WASM 的在线编辑、导入导出、权限切换、多实例与实例缓存。
但这只是起点。v7 跑通之后,真正落地还会碰到几类硬问题:
- 工程化问题:(
权限/多实例/导出/事件监听/批注修订等api并没有进行封装。逻辑分布在custom func hook 和class实例中)。 - 字体问题:
onlyoffice web v7版本 不支持 字体自定义注册 - 更新问题:
onlyoffice静态资源版本已经更新到 v9 版本,但是v7/v9版本api差异巨大,不能平滑过渡版本(github.com/electroluxc…
因此 在v9版本中,架构收敛到 EditorManager 单实例模型,补齐批注修订,并把工程化实例注册,文档能力做成了可复用的标准方案。
开发者只需要 拿到 public/packages/onlyoffice/9.3.0 和 src/components/onlyoffice-web-comp 这两个文件夹 就可以在 任何前端项目中接入这套 web 预览编辑 office 的组件
🔗 GitHub:onlyoffice-web-comp
🌐 在线体验:onlyoffice-web-comp.vercel.app
核心功能
| 能力 | 说明 |
|---|---|
| 📄 文档编辑 | Word / Excel / PPT,基于 OnlyOffice 9.3.0 |
| 🔄 格式转换 | 本地 WASM 转换,Worker 后台跑,界面不卡顿 |
| 💾 导入导出 | 上传本地文件,一键下载编辑结果 |
| 🔒 权限切换 | 只读 ↔ 编辑,即时生效,不用重载编辑器 |
| 🌏 多语言 | 中英文界面一键切换 |
| 🎯 多实例 | 多个编辑器并排或 Tab 切换,互不干扰 |
| 📝 批注修订 | Word 审校场景开箱即用(v9 新增) |
架构设计:EditorManager 单实例模型
不用为 Word、Excel、PPT 各写一套逻辑。一个 EditorManager 实例绑定一个容器,打开、切换、导出、批注、修订,都从它出发。
classDiagram
class EditorManager {
+containerId
+instanceId
+create() 打开/切换文档
+export() 导出
+setReadOnly() 只读
+addComment() 批注
+setTrackRevisions() 修订
}
class OnlyOfficeManager {
+openDocument()
+downloadExport()
+toggleLanguage()
}
class EditorManagerFactory {
+getDefault()
+get(containerId)
}
class OnlyOfficeManagerFactory {
+open(options, document)
}
EditorManagerFactory --> EditorManager : 按 containerId 创建
OnlyOfficeManager *-- EditorManager : 组合
OnlyOfficeManagerFactory --> OnlyOfficeManager : 按 containerId 缓存
OnlyOfficeManagerFactory ..> EditorManagerFactory : 获取底层实例
三层架构:
| 层级 | 组件 | 职责 |
|---|---|---|
EditorManager | 核心引擎 | 一个槽位 = 一份文档的完整生命周期 |
OnlyOfficeManager | 门面层 | 封装初始化、导出、语言等页面级 API |
| 工厂单例 | 实例管理 | 按 containerId 创建或复用,支持多容器并行 |
日常接入用 OnlyOfficeManager 就够了;需要批注、修订、事件订阅等底层能力时,再拿 getEditor() 操作 EditorManager。
// 推荐:业务门面
const manager = await OnlyOfficeManager.create({ containerId: ONLYOFFICE_ID, ... });
await manager.openFile(docxFile);
await manager.openFile(xlsxFile); // 同一实例,切换文档类型
await manager.downloadExport();
// 进阶:底层实例
const editor = manager.getEditor();
editor.addComment("请修改");
editor.setTrackRevisions(true);
// 多实例:各自独立,媒体与事件互不影响
const editor1 = editorManagerFactory.get("editor-1");
const editor2 = editorManagerFactory.get("editor-2");
组件库不绑定 React,直接复用
src/components/onlyoffice-web-comp;接入示例见src/components/onlyoffice-web-demo/。
WASM 格式转换:Web Worker + x2t
选文件 → 浏览器读取 → Worker 加载 WASM → 转换 → 编辑器打开。重活全在后台线程,页面照样流畅。
v9 的 x2t 转换跑在 Web Worker 里,资源做了 Brotli 预压缩,Worker 内自动解压,不用配服务端 Content-Encoding。
// 主线程只发指令,计算交给 Worker
class X2tConverter {
async convert(params: X2tConvertParams) {
return this.sendMessage("convert", params); // ArrayBuffer 零拷贝传递
}
}
// 导出时:bin → Office 文件
const result = await convertBinToDocument(binData, fileName, FILE_TYPE.XLSX, media);
支持格式: Word(docx/doc/odt/rtf/txt)· Excel(xlsx/xls/ods/csv)· PPT(pptx/ppt/odp)
接入指南
DOM 容器挂载
<div class="onlyoffice-container">
<div id="onlyoffice-editor" />
</div>
实例初始化与 API 调用
import { OnlyOfficeManager, ONLYOFFICE_ID, FILE_TYPE } from "@/components/onlyoffice-web-comp";
const manager = await OnlyOfficeManager.create({
containerId: ONLYOFFICE_ID,
fileType: FILE_TYPE.DOCX,
defaultFileName: "New_Document.docx",
readOnly: false,
lang: "zh",
});
await manager.openFile(uploadedFile); // 打开本地文件
await manager.openNew("New_Document.docx"); // 新建空白文档
await manager.downloadExport(); // 导出下载
manager.toggleReadOnly(); // 切换只读
await manager.toggleLanguage(); // 切换语言
已有文件时,用 createWithFile 直接挂载,不会先闪一下空白页:
const manager = await OnlyOfficeManager.createWithFile(
{ containerId: ONLYOFFICE_ID, fileType: FILE_TYPE.XLSX, defaultFileName: "test.xlsx" },
file,
);
多实例并行部署
const manager1 = await onlyOfficeManagerFactory.open(
{ containerId: "editor-1", fileType: FILE_TYPE.DOCX, defaultFileName: "Doc1.docx" },
{ fileName: "Doc1.docx", isNew: true },
);
const manager2 = await onlyOfficeManagerFactory.open(
{ containerId: "editor-2", fileType: FILE_TYPE.XLSX, defaultFileName: "Doc2.xlsx" },
{ fileName: "Doc2.xlsx", isNew: true },
);
await manager1.downloadExport();
await manager2.downloadExport();
onlyOfficeManagerFactory.destroyAll();
每个实例通过唯一 containerId 隔离——容器、媒体资源、SAVE_DOCUMENT 事件互不串台。
API 参考
OnlyOfficeManager(门面层)
| 分类 | 方法 |
|---|---|
| 文档 | openFile · openNew · openDocument · isReady |
| 权限 | getReadOnly · setReadOnly · toggleReadOnly |
| 语言 | getLanguage · setLanguage · toggleLanguage |
| 导出 | exportDocument · exportAsBlob · downloadExport |
| 其他 | onLoadingChange · getEditor · destroy |
EditorManager(核心层)
| 分类 | 方法 |
|---|---|
| 核心 | create · export · exists · destroy |
| 状态 | getReadOnly · setReadOnly · getFileName · getInstanceId |
| 媒体 | updateMedia · getMedia |
| 事件 | subscribe({ type, fn }) — 批注、修订等 Word SDK 回调 |
export() 返回 { fileName, fileType, binData, media?, instanceId? }。只读模式下直接返回缓存,不走 downloadAs。
Demo 路由
| 路由 | 场景 |
|---|---|
/docs/base | Word 单实例 |
/excel/base | Excel 单实例 |
/ppt/base | PowerPoint 单实例 |
/multi/base | 多实例并排 |
/multi/tabs | 多实例 Tab 切换 |
加 ?locale=en 或 ?locale=zh 切换界面语言。
版本迁移:v7 → v9
| 项目 | v7 | v9 |
|---|---|---|
| 推荐 API | createEditorView | OnlyOfficeManager |
| 只读切换 | 重建编辑器 | 即时切换,无需重载 |
| 格式转换 | 主线程 WASM | Worker + Brotli,不卡 UI |
| SDK | 旧版 | 9.3.0 |
| 批注修订 | ❌ | ✅ |
| 保存事件 | SAVE_DOCUMENT | 新增轻量 ONSAVE |
参考项目
- Qihoo360/se-office — 开放标准办公套件
- cryptpad/onlyoffice-x2t-wasm — WASM 文件转换
- ranuts/document — 静态资源方案参考
方案特性总结
- 🚀 纯前端 — 零后端依赖,静态部署即可
- 🔒 数据本地化 — 文件不上传服务器,隐私有保障
- ⚡ 性能友好 — Worker 后台转换,Brotli 压缩加速加载
- 🎯 多实例隔离 — 并排、Tab 随便摆,资源不打架
- 📝 审校就绪 — Word 批注修订,拿来就能用
如果这个项目对你有帮助,欢迎 Star 和 Fork。
延伸阅读: