首页
/ Tiptap 编辑器深度解析:Headless 富文本框架的扩展架构、核心 API 与本地实战

Tiptap 编辑器深度解析:Headless 富文本框架的扩展架构、核心 API 与本地实战

2026-09-05 13:23:32作者:邬祺芯Juliet

Tiptap 是一个无界面(headless)、框架无关的富文本编辑器框架,基于 ProseMirror 构建,通过扩展(Extension)机制实现从基础文本样式到复杂块级编辑能力的自由组合。本文以 Tiptap 仓库根目录 README.md 为主线,结合 packages/core 的源码实现与 demos 中的真实示例,完整梳理其设计哲学、包生态结构、EditorOptions 配置体系与扩展开发机制,帮助你在本地搭建、运行并二次开发 Tiptap 编辑器。

一、Tiptap 是什么:Headless、框架无关、扩展驱动

根据仓库 README.md 的定义,Tiptap Editor 是一个 headless、framework-agnostic(框架无关)的富文本编辑器,核心特征有四点:

  1. Headless(无内置界面):Tiptap 不附带任何固定 UI,不需要类名覆盖或代码 hack 来改样式,设计自由度完全交给使用者;
  2. 框架无关:同一套核心可以在 Vue、React、Svelte 或纯 JavaScript 环境中集成,无兼容性问题;
  3. 基于扩展:从简单文本样式到拖拽块编辑,功能均由扩展提供。README 提到官方文档与社区提供了上百个扩展可供选择;
  4. UX 完全可控:编辑器允许开发者自定义扩展与节点(Node),把交互体验的定义权交给应用层。

在源码层面,这一理念对应 packages/core 包——它被描述为 headless rich text editor(见 packages/core/package.json),当前版本为 3.30.3。Tiptap 的底层构建在 ProseMirror 之上:仓库通过 packages/pm 这一 workspace 包对 ProseMirror 各子模块(model、state、view、schema-list、tables 等)做统一再导出,所有 @tiptap/* 包都依赖 @tiptap/pm 而非直接引用上游包,从包管理角度屏蔽了 ProseMirror 的模块拆分细节。

此外,README 指出 Tiptap 与开源协作后端 Hocuspocus 共同构成 Tiptap Suite 的基础,后者围绕 Yjs 的 CRDT 能力构建。本仓库中对应的是 packages/extension-collaboration 等协作相关扩展包。

二、仓库结构:一个多包(monorepo)生态

从仓库目录结构看,Tiptap 是一个典型的 pnpm workspace monorepo,分为四大板块:

2.1 packages/:核心与官方扩展

这是最重要的部分,包含:

2.2 packages-deprecated/:废弃包的兼容层

packages-deprecated 下保留了 extension-character-countextension-historyextension-placeholderextension-task-item 等旧包。例如历史上独立的 ListItem / TableCell / TaskList 等,如今已合并进 packages/extension-listpackages/extension-table 这类聚合包中——废弃包的存在说明 Tiptap 在扩展粒度上做过重组,但仍维持向后兼容。

2.3 demos/:可运行的官方示例应用

demos 是一个 Vite + 多框架的示例站点,目录按功能域划分:Examples/(默认、协作、无障碍等)、Extensions/(DragHandle、FindAndReplace、TableOfContents 等)、Marks/(Bold、Link、Underline…)、Nodes/(Heading、CodeBlock、Youtube…)、Tutorials/(从 textarea 到 Tiptap 再到 Yjs 协作的渐进式教程)、Experiments/ 等。每个示例通常包含 React(.jsx/.tsx)与 Vue(.vue)两套实现,这正是 README 所宣称的"框架无关"的直观体现。

2.4 scripts/ 与工程配置

根目录 package.json 定义了整套工程脚本:pnpm dev(启动 demos)、pnpm buildpnpm test:unit(基于 vp test)、pnpm test:e2e(Playwright)、pnpm lint / pnpm format(oxlint / oxfmt)等。环境要求为 Node >= 24,包管理器固定为 pnpm@11.2.2packageManager 字段声明)。

三、核心引擎:@tiptap/core 的 Editor 与 EditorOptions

3.1 Editor 类

编辑器入口是 packages/core/src/Editor.ts 中的 Editor 类(自 L59 起)。关键实现事实:

  • Editor 继承自 EventEmitter(编辑器事件系统),内部持有 CommandManager(命令系统)、ExtensionManager(扩展管理器)、ProseMirror 的 SchemaEditorViewEditorState
  • 默认选项在 L92-L120 定义,例如 content: ''extensions: []autofocus: falseeditable: trueenableInputRules: trueenablePasteRules: true 等;
  • 实例具备 instanceId(随机生成)、isInitialized 标志与 extensionStorage(各扩展的持久化存储);
  • 内置核心扩展在构造时自动注入,包括 ClipboardTextSerializerCommandsDeleteDropEditableFocusEventsKeymapPasteTabindexTextDirection(见 Editor.ts L11-L22 的导入),它们分别对应键盘快捷键、焦点/失焦事件、粘贴处理等基础行为。

3.2 EditorOptions 完整配置项

完整的选项类型定义在 packages/core/src/types.tsEditorOptions 接口中,逐项说明如下:

选项 类型 / 取值 说明
element Element | { mount: HTMLElement } | ((editor) => void) | null 编辑器挂载目标:传 Element 则追加到该元素;传 null 则不自动挂载;传函数则由其自行放置编辑器 DOM
content Content 初始内容,支持 HTML 字符串、JSON 对象或 JSON 数组
extensions Extensions 使用的扩展列表
injectCSS boolean 是否注入基础 CSS
injectNonce string | undefined 注入样式时使用的 CSP nonce
autofocus FocusPosition 初始聚焦位置
editable boolean 是否可编辑(只读模式设为 false
textDirection 'ltr' | 'rtl' | 'auto' | undefined 全文本方向策略:auto 会按内容检测设置 dir 属性
editorProps EditorProps 透传给 ProseMirror EditorView 的 props
parseOptions ParseOptions 内容解析选项
coreExtensionOptions 对象 核心扩展细粒度配置:clipboardTextSerializer.blockSeparatortabindex.valuedelete.async / delete.filterTransaction
enableInputRules EnableRules 是否启用输入规则(如 Markdown 快捷输入)
enablePasteRules EnableRules 是否启用粘贴规则
enableCoreExtensions boolean | Partial<Record<...>> 布尔值全量开关;也可传对象按名称关闭单个核心扩展,如 { keymap: false }
enableContentCheck boolean,默认 false 初始化时检查内容合法性,非法时触发 contentError 事件
emitContentError boolean,默认 false 不做阻断性检查,但保留内容并触发 contentError 警告
onBeforeCreate / onCreate / onMount / onUnmount 回调 生命周期钩子
onUpdate / onSelectionUpdate / onTransaction 回调 内容、选区、事务变化回调
onFocus / onBlur 回调 焦点事件回调
onDestroy 回调 销毁回调
onPaste / onDrop / onDelete 回调 粘贴、拖入、删除的内容拦截回调
enableExtensionDispatchTransaction boolean,默认 true 是否允许扩展自定义 dispatchTransaction 钩子

Editor.ts L119-L120 还展示了 onContentError 的默认行为是 直接抛出 errorthrow error),意味着开启 enableContentCheck 后若不加自定义处理,非法内容会直接中断初始化——这是使用时的一个重要注意点。

3.3 core 包的公开 API

packages/core/src/index.ts 可以看出 @tiptap/core 的完整导出面:EditorExtensionNodeMarkNodeViewMarkViewInputRulePasteRuleCommandManagerTracker,以及内置的 commands 命名空间、jsx-runtimecreateElement / Fragment,用于在 JS 环境中以 JSX 描述文档节点)等。扩展作者几乎只需要依赖这些导出即可编写新扩展。

四、扩展机制:Extension 的 create / configure / extend 三件套

README 强调 Tiptap "通过扩展定制与扩展能力",其机制实现于 packages/core/src/Extension.ts(L16-L59):

  • Extension.create(config)(L27-L33):静态工厂方法,接受配置对象或返回配置对象的函数,返回 Extension 实例。这是所有自定义扩展的入口;
  • configure(options)(L35-L37):用新选项创建同一扩展的配置化副本,常用于"给扩展传参"而不改变其行为;
  • extend(extendedConfig)(L39-L58):基于现有扩展派生新扩展,可在派生时覆盖 addCommandsaddKeyboardShortcutsaddAttributes 等任意配置段,同样支持函数式配置。

Extension 继承自 Extendablepackages/core/src/Extendable.ts),后者统一处理 name / option / storage 合并逻辑;NodeMark 类(packages/core/src/Node.tspackages/core/src/Mark.ts)分别继承该体系,形成"扩展—节点—标记"三层结构。

以仓库内一个最小扩展为例,packages/extension-strike/src 展示了如何定义一个 Mark 类扩展并注册 strike 命令与 HTML 序列化规则;而 packages/extension-heading/src 则展示了 Node 扩展如何声明 level 属性。两者都可配合 __tests__ 目录下的用例(如 packages/extension-strike/tests 的测试文件)验证行为。

五、快速上手:StarterKit 与仓库内真实示例

5.1 StarterKit 包含什么

packages/starter-kit/package.json 的依赖列表就是它的完整内容:extension-documentextension-paragraphextension-text(文档结构三件套)、extension-headingextension-blockquoteextension-bullet-list / extension-ordered-list / extension-list / extension-list-item(标题、引用与列表)、extension-bold / extension-italic / extension-strike / extension-underline / extension-code(行内标记)、extension-code-blockextension-horizontal-ruleextension-hard-breakextension-link,以及 extension-dropcursor / extension-gapcursor(拖放与空位光标)等。也就是说,一个"基础编辑器"≈ 一个 StarterKit

5.2 官方默认示例(React 版)

demos/src/Examples/Default/React/index.tsx 展示了标准用法:

import { EditorContent, useEditor } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'
import { TextStyleKit } from '@tiptap/extension-text-style'

const extensions = [TextStyleKit, StarterKit]

export default () => {
  const editor = useEditor({
    extensions,
    content: `<h2>Hi there,</h2><p>this is a <em>basic</em> example…</p>`,
  })

  return (
    <>
      <MenuBar editor={editor} />
      <EditorContent editor={editor} />
    </>
  )
}

要点:内容可以是 HTML 字符串;MenuBar 是演示用的自绘工具栏(headless 的体现);同一示例在 demos/src/Examples/Default/Vue 中有 Vue 实现,在 demos/src/Examples/Default/Svelte 中有 Svelte 实现,可对比三种框架适配层的 API 差异(useEditor 响应式 ref 等)。

纯 JS / 无框架场景下,直接使用 @tiptap/corenew Editor({ element, extensions, content })element 参数语义见前文 EditorOptions 表。

六、在本地运行 Tiptap 仓库

以下命令均基于根目录 package.jsonscripts 定义(要求 Node >= 24、pnpm 11.2.2):

pnpm install        # 安装 workspace 依赖(prepare 阶段会自动运行 vp config)
pnpm dev            # 启动 demos 应用(vp run start)
pnpm build          # 递归构建所有 packages(vp run -r build)
pnpm test:unit      # 运行单元测试(vp test)
pnpm test:e2e       # Playwright 端到端测试(chromium)
pnpm lint           # oxlint 静态检查

几个值得关注的工程化细节:

七、协作编辑与 Tiptap Suite

README 专设 "Make your editor collaborative" 一节:协作能力由开源后端 Hocuspocus 提供,其核心是 Yjs CRDT;编辑器与 Hocuspocus 共同构成 Tiptap Suite 的基础。

在仓库中对应的实现有:

README 同时提到 Pro Extensions(商用订阅扩展),覆盖协作编辑、评论、版本管理、文档转换与 AI 相关功能;仓库中 packages/ai-toolkit 则代表了开源侧的 AI 工具能力(如 streaming-reveal 流式输出渐显,见 packages/ai-toolkit/src/streaming-reveal.ts 及其测试 packages/ai-toolkit/tests/streaming-reveal.spec.ts)。

八、社区、贡献规范与许可

  • 贡献指南CONTRIBUTING.md 要求提交前运行测试与 linter、为包变更附带 Changeset、PR 关联已指派的 issue、AI 辅助生成内容须显式披露、30 天无响应会关闭 PR 等;
  • 安全:漏洞报告遵循 SECURITY.md
  • 许可:项目采用 MIT 许可证,详见 LICENSE.md
  • 社区:README 引导使用者通过 GitHub Discussions 进行讨论(具体入口以仓库页面的 Discussions 区为准)。

九、小结

Tiptap 的技术定位可以概括为:"ProseMirror 之上的声明式扩展层"。packages/core 提供 Editor + Extension 双核心与完整的事件/命令体系;packages/pm 屏蔽 ProseMirror 模块细节;extension-* 包把每个功能单元化;demos 则证明同一 API 可以在 React、Vue 2/3、Svelte 与纯 JS 中互换运行。理解 EditorOptionspackages/core/src/types.ts)与 Extension.create / configure / extendpackages/core/src/Extension.ts)这两块,就掌握了在 Tiptap 生态中搭建、定制乃至贡献新扩展所需的全部核心知识。

登录后查看全文
热门项目推荐
相关项目推荐