端到端演练:导入、编辑、导出
本指南将带你从一个空项目开始,端到端完成可用的 DOCX 导入和导出。我们将使用 ConvertKit 配置编辑器,接入一个文件输入,通过 Conversion 服务上传 DOCX,并将文档再导出回 DOCX。如果你想要分页式编辑器,第 6 步还会展示如何在此基础上叠加 Pages 扩展。
前置条件
- 一个带有 Pro 方案或试用 的 Tiptap 账户。
- 可访问 Tiptap 的私有 npm 仓库。
- 来自 Convert 设置页面 的 App ID 和 secret key。
1. 安装 Conversion 技术栈
npm install @tiptap-pro/extension-convert-kit \
@tiptap-pro/extension-import-docx \
@tiptap-pro/extension-export-docx为什么总是用 ConvertKit
ConvertKit 是 Conversion 的标准编辑器套件。它注册了 DOCX 感知的 schema —
段落间距、图片裁剪、表格单元格格式 — 导入服务会生成这些内容,而导出服务会消费这些内容。
不要用 StarterKit 替代;否则在往返转换时,这些属性会丢失。
2. 使用 ConvertKit 设置编辑器
import { Editor } from '@tiptap/core'
import { ConvertKit } from '@tiptap-pro/extension-convert-kit'
import { ImportDocx } from '@tiptap-pro/extension-import-docx'
import { ExportDocx } from '@tiptap-pro/extension-export-docx'
const editor = new Editor({
element: document.querySelector('#editor'),
extensions: [
ConvertKit,
ImportDocx.configure({
appId: 'YOUR_APP_ID',
token: 'YOUR_JWT', // 在服务端生成
}),
ExportDocx.configure({
onCompleteExport: (result) => {
// 第 5 步会在这里补充
},
}),
],
})生成 JWT
始终在你的服务器上生成 JWT。不要把 secret key 嵌入浏览器代码中。有关 token 形状的细节,请参阅 Conversion 安装指南。
3. 接入 DOCX 导入
在页面上添加一个文件输入,并将选中的文件传给编辑器的 import 命令:
<input type="file" id="docx-input" accept=".docx" />const input = document.querySelector<HTMLInputElement>('#docx-input')!
input.addEventListener('change', () => {
const file = input.files?.[0]
if (!file) return
editor.chain().focus().importDocx({ file }).run()
})这就是完整流程。转换服务接收 DOCX,返回 Tiptap JSON,编辑器使用 ConvertKit 的 schema 将其渲染出来。
4. 自行处理导入结果
默认导入会替换编辑器内容。若要校验、转换或展示错误,可以传入 onImport 回调:
editor
.chain()
.focus()
.importDocx({
file,
onImport(context) {
if (context.error) {
console.error('导入失败:', context.error)
return
}
// 在这里检查或转换 `context.content`。若要原样使用它,请调用 setEditorContent()。
// 若要替换为你自己的转换结果,请使用 editor.commands.setContent()。
context.setEditorContent()
},
})
.run()content 对象是普通的 Tiptap JSON。记录它是最快了解转换器实际产出的方式——当某个功能渲染异常时尤其有用。
5. 配置 DOCX 导出
在 onCompleteExport 回调中补上下载触发逻辑:
ExportDocx.configure({
onCompleteExport: (result) => {
const blob = new Blob([result], {
type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
})
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = 'document.docx'
a.click()
URL.revokeObjectURL(url)
},
exportType: 'blob', // 默认值;这里显式写出以便更清楚
})从按钮触发导出:
document.querySelector('#export')!.addEventListener('click', () => {
editor.commands.exportDocx()
})DOCX 导出是客户端执行的:不需要 JWT、不需要 App ID、也不需要 API 调用。转换发生在扩展本身内部。
往返转换不会逐字节完全一致
一些功能(上下标标记、段落行高、浮动表格)目前往返转换并不完美。 请查看完整的 功能支持矩阵。 不要指望 DOCX → 编辑 → DOCX 的循环会生成与原文件逐字节完全一致的结果。
6. (可选)添加 Pages 以实现分页渲染
如果你希望编辑器看起来像真正的文档——有分页边界、页眉页脚、页面格式——可以在同一套配置上继续添加 Pages 扩展 和 PagesTableKit:
npm install @tiptap-pro/extension-pages-tablekit @tiptap-pro/extension-pagesimport { ConvertKit } from '@tiptap-pro/extension-convert-kit'
import { TableKit } from '@tiptap-pro/extension-pages-tablekit'
import { Pages } from '@tiptap-pro/extension-pages'
import { ImportDocx } from '@tiptap-pro/extension-import-docx'
import { ExportDocx } from '@tiptap-pro/extension-export-docx'
const editor = new Editor({
extensions: [
ConvertKit.configure({ table: false }), // 禁用 ConvertKit 自带的表格
TableKit, // 改用适合分页的表格
Pages.configure({
pageFormat: 'A4',
header: '我的文档',
footer: '第 {page} 页,共 {total} 页',
}),
ImportDocx.configure({ appId: 'YOUR_APP_ID', token: 'YOUR_JWT' }),
ExportDocx.configure({ onCompleteExport: /* ... */ }),
],
})与第 2 步的差异:
- 禁用 ConvertKit 的表格(
{ table: false });ConvertKit 内置的表格不适合分页。 - 添加来自
extension-pages-tablekit的TableKit。 - 添加
Pages.configure({...})。
现在导入的文档会按其页眉、页脚和页面格式进行渲染。导出路径保持不变——editor.commands.exportDocx() 仍然有效,导出的 DOCX 会保留 Pages 覆盖层中的页眉和页脚。
Pages 的重要限制
在发布前请阅读 Pages 限制说明 页面。比页面还高的表格和其他不可拆分块可能会让布局进入无限循环。
预期结果
- 导入始终通过服务完成。 即使是很小的 DOCX,也会发送到 Conversion 服务进行解析。该服务需要
appId和 JWT。 - 导出是本地完成的。 DOCX 导出在浏览器中运行;不需要认证,也不会发起网络请求。
- 往返漂移是真实存在的。 对于矩阵中无法往返转换的功能(如下标标记、行高),要提前规划——要么在编辑器 schema 中避免使用这些功能,要么接受这种差异。
不要期望
- OCR 或扫描版 PDF 处理。 Conversion 处理的是结构化文档,不是页面位图输入。
- 此方案中的实时协作。 如果你需要多人编辑,请搭配 Tiptap Collaboration 产品——在与 Pages 结合时,请参阅 向 Pages 添加协作。
- 异步任务控制。 该服务对每个请求都是同步响应。对于非常大的文档,请自行在你的系统中规划超时。