[onlyoffice-v9]纯前端怎么实现编辑预览office

纯前端 OnlyOffice 集成方案(v9)

v7 跑通了,v9 解决了工程化落地难题。

上一篇里,我们用 v7 跑通了「纯前端 Office」的核心链路------基于 WASM 的在线编辑、导入导出、权限切换、多实例与实例缓存。

但这只是起点。v7 跑通之后,真正落地还会碰到几类硬问题:

  • 工程化问题 :(权限/多实例/导出/事件监听/批注修订 等api并没有进行封装。并且大部分web onlyoffice 仓库耦合严重,并不能做到任何项目拿来即用)。
  • 字体问题 :onlyoffice web v7 版本 不支持 字体自定义注册,一些公文和要求高的场景没办法自定义字体。。。。
  • 更新问题 :onlyoffice 静态资源版本已经更新到 v9 版本,但是v7/v9版本api差异巨大,并不能平滑过渡到v9版本,怎么自动化更新到最新onlyoffice的最新静态资源(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 新增)

方案差异化:对比常见 OnlyOffice Web 集成

社区里常见的 OnlyOffice Web 方案,大致分两类:一是依赖 Document Server 的 Docs API 模式,二是参考 cryptpad/onlyoffice-x2t-wasm、ranuts/document 等项目的纯静态集成。后者能跑起来,但往往卡在字体 和工程维护上------打开带特殊字体的公文排版错乱,SDK 一升级就得手工 diff 静态资源。

我们在 v7 实践基础上,针对这两个痛点做了体系化建设。

字体注册:__custom_font_registry__ 全链路

纯前端 OnlyOffice 的字体问题,不只是「放几个 TTF 文件」这么简单。Word / Excel / PPT 各走一套字体管线,SDK 初始化后还会 delete __fonts_files;x2t 转换时字体名对不上,导入导出也会丢字形。

本方案的字体体系分三层:

环节 做法 解决的问题
声明式注册 AllFonts.js 中维护 __custom_font_registry__,id → 别名列表 一处声明 不用手改上千行的 __fonts_files / __fonts_infos
构建工具链 ttf-to-catalog-font.mjs 将 TTF/OTF 转为 OnlyOffice catalog 线格式,输出到 fonts/{id} 字体产物标准化,可脚本化批量生成
运行时补丁 注册表自动同步进 SDK 字体索引;Word/Cell/Slide 三套管线分别补丁;多实例加载用快照兜底 多编辑器并存、后加载的 Excel 不会丢自定义字体
转换层对齐 x2t Worker 支持 fontAliases / fontExportAliases,导入时重写文档内字体名,导出时还原 打开 → 编辑 → 导出,字形全程一致
加载层保障 editor-manager.ts 对 AllFonts.js / libfont/ / fonts/ 走原生 XHR,绕过代理 字体二进制同步读取不被 fetch 代理打断

注册示例:

javascript 复制代码
// public/packages/onlyoffice/9.3.0/sdkjs/common/AllFonts.js
window["__custom_font_registry__"] = {
  "1001": [
    "仿宋_GB2312",
    "Slidefu",
    "Slidefu Regular",
    "演示佛系体",
  ],
};
bash 复制代码
# TTF → catalog 线格式
node public/packages/onlyoffice/9.3.0/fonts/ttf-to-catalog-font.mjs --id 1001 --verify

对比常见方案:多数项目要么不管字体(打开就 fallback),要么手动改 AllFonts.js 数组、升级 SDK 后全部重来。我们把「注册 → 产物 → 运行时 → 转换」串成闭环,升级 SDK 只需合并 registry 和 fonts/{id} 目录,其余由脚本和补丁代码承接。

工程化管理:可维护的 SDK 升级流程

纯静态 OnlyOffice 集成的隐性成本在维护:SDK 版本一升,sdkjs / web-apps / fonts 全量替换,接入层的字体补丁、批注修订、x2t 格式映射不会自动跟过来。

本项目的工程化建设:

维度 实践
SDK 提取脚本 scripts/extract-documentserver-assets.sh 从 DocumentServer Docker 镜像一键导出静态资源,版本号与目录结构对齐
升级检查清单 脚本执行后自动打印 patch 复查项:字体 registry、批注修订 API、x2t 格式映射、静态路径配置
库与演示分离 onlyoffice-web-comp(框架无关组件库)+ onlyoffice-web-demo(Next.js 接入参考),业务只依赖前者
版本化静态资源 public/packages/onlyoffice/9.3.0/ 按版本隔离,配合 STATIC_RESOURCE 统一路径入口
分层文档 组件库 docs/00--07 覆盖接入、API、事件、批注修订,README 只管仓库级说明
工具脚本内聚 字体转换、批注修订 API 回归测试等脚本随组件库/仓库维护,不散落在业务代码里
bash 复制代码
# 升级 OnlyOffice 静态资源(示例)
./scripts/extract-documentserver-assets.sh public/packages/onlyoffice/9.3.0

对比常见方案:很多仓库把 SDK 文件直接 commit 进 public/,没有提取脚本、没有升级 checklist,版本迁移基本靠「全量替换 + 肉眼 diff」。我们把 v7 踩过的坑固化成流程------换 SDK 有脚本、换完有清单、接入有文档,长期维护成本可控。


架构设计: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。

typescript 复制代码
// 推荐:业务门面
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。

typescript 复制代码
// 主线程只发指令,计算交给 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 容器挂载

html 复制代码
<div class="onlyoffice-container">
  <div id="onlyoffice-editor" />
</div>

实例初始化与 API 调用

typescript 复制代码
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 直接挂载,不会先闪一下空白页:

typescript 复制代码
const manager = await OnlyOfficeManager.createWithFile(
  { containerId: ONLYOFFICE_ID, fileType: FILE_TYPE.XLSX, defaultFileName: "test.xlsx" },
  file,
);

多实例并行部署

typescript 复制代码
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

参考项目


方案特性总结

  • 🚀 纯前端 --- 零后端依赖,静态部署即可
  • 🔒 数据本地化 --- 文件不上传服务器,隐私有保障
  • ⚡ 性能友好 --- Worker 后台转换,Brotli 压缩加速加载
  • 🎯 多实例隔离 --- 并排、Tab 随便摆,资源不打架
  • 📝 审校就绪 --- Word 批注修订,拿来就能用

如果这个项目对你有帮助,欢迎 Star 和 Fork。

延伸阅读:

相关推荐
IT_Octopus1 小时前
【零基础入门 LLM 开发 · Day 11】:ChatOpenAI vs init_chat_model——一行换供应商
java·前端·javascript
deli0071 小时前
撒下 200 个种子点,平面为什么自己长成蜂窝?Voronoi 图实验室
前端
King禅1 小时前
JavaScript 调试实战指南:从 console.log 到 DevTools 深度运用
javascript
用户813267933251 小时前
行情数据晚到几秒,会让量化策略失去优势吗?从信号时间到回测偏差
后端·github·api
大文说跨境1 小时前
多账号环境隔离方案技术选型:指纹浏览器、VPS 与云手机的三种架构对比
java·开发语言·前端
MicrosoftReactor1 小时前
技术速递|使用 GitHub Security Lab Taskflow Agent 实现 AI 驱动的模糊测试
人工智能·ai·github·copilot·agent·模糊测试·ai-agent
咕白m6251 小时前
使用 C# 将 TIFF 转换为 PDF
前端·c#
全栈项目管理程序猿1 小时前
ArcGIS JS 基础教程(28):图层渲染顺序管理
前端·javascript
QuantiCore_IO1 小时前
从请求风暴到可维护的数据管道:量化系统为什么需要批量接口?
后端·github·api
huakoh1 小时前
MCP 工具报错走哪条通道:三条探针的最小复现检查
前端