Tiptap 是一个基于 ProseMirror 的现代化、可扩展的富文本编辑器框架,专为构建功能丰富的文本编辑器而设计。它不是一个开箱即用的"Word 替代品",而是一个强大的开发工具包,让开发者可以高度定制地构建自己需要的编辑器。
简单来说,Tiptap 能让你在 Web 应用中构建出类似于 Notion、Trello、Linear、GitHub 评论框、CMS 后台编辑器 等产品中的那种交互式文本编辑体验。
Tiptap 的主要能力和应用场景:
- 核心文本编辑与格式化
基础富文本功能:加粗、斜体、下划线、删除线、代码块等。
标题、段落、列表(有序/无序)、任务列表、引用块。
文本对齐、行高、缩进。
- 高级内容类型与布局
表格:支持创建、编辑、调整表格。
代码块:支持语法高亮(配合 Prism.js 或 Highlight.js)。
图片与媒体:上传、拖拽插入、调整大小、对齐。
水平分割线。
可定制的"文档":类似 Notion,可以嵌入多种内容块。
- 协同编辑
内置对 Y.js 协议的支持,可以相对容易地实现实时协同编辑(多人同时编辑同一文档,看到彼此的光标和更改),这是其核心亮点之一。
- 强大的扩展性
核心能力:Tiptap 本身非常精简,所有高级功能(如表单、图片、表格等)都通过扩展(Extension) 来实现。你可以选择你需要的功能,保持编辑器轻量。
自定义节点和标记:你可以创建自己的内容类型,比如"商品卡片"、"用户提及"、"特殊注解块"等。
自定义插件:可以注入任何功能,如快捷键、拖放处理、粘贴规则等。
- 与现代技术栈无缝集成
专为 Vue.js、React、Svelte 和原生 JavaScript 项目设计,有对应的官方包(@tiptap/vue-3, @tiptap/react, @tiptap/svelte)。
状态管理清晰,易于与你的应用状态(如 Vuex/Pinia, Redux, Zustand)集成。
- 出色的用户体验功能
气泡菜单:选中文本时出现的浮动工具栏(类似 Medium)。
浮动菜单:在空行或特定位置出现的"+"号工具菜单(类似 Notion)。
斜杠命令:输入 / 唤起命令菜单,快速插入元素(非常流行的交互模式)。
历史记录:撤销/重做。
Markdown 快捷键:在输入时使用 Markdown 语法(如 **加粗**、# 标题)快速格式化。
- 数据输出灵活
可以输出 HTML、JSON、Markdown 或纯文本。其原生文档模型是基于 JSON 的,这使得保存、传输和恢复复杂内容(包括自定义节点)非常方便。
拖拉表格行

设置行数据绑定

简单的使用例子
<template>
<div class="editor">
<div class="menu-bar">
<button
@click="editor.chain().focus().undo().run()"
:disabled="!editor.can().undo()"
>
撤销
</button>
<button
@click="editor.chain().focus().redo().run()"
:disabled="!editor.can().redo()"
>
重做
</button>
<button
@click="editor.chain().focus().setParagraph().run()"
:class="{ 'is-active': editor.isActive('paragraph') }"
>
段落
</button>
<button
@click="editor.chain().focus().toggleHeading({ level: 1 }).run()"
:class="{ 'is-active': editor.isActive('heading', { level: 1 }) }"
>
H1
</button>
<button
@click="editor.chain().focus().toggleHeading({ level: 2 }).run()"
:class="{ 'is-active': editor.isActive('heading', { level: 2 }) }"
>
H2
</button>
<button
@click="editor.chain().focus().toggleHeading({ level: 3 }).run()"
:class="{ 'is-active': editor.isActive('heading', { level: 3 }) }"
>
H3
</button>
<button
@click="editor.chain().focus().toggleBold().run()"
:class="{ 'is-active': editor.isActive('bold') }"
>
粗体
</button>
<button
@click="editor.chain().focus().toggleItalic().run()"
:class="{ 'is-active': editor.isActive('italic') }"
>
斜体
</button>
<button
@click="editor.chain().focus().toggleStrike().run()"
:class="{ 'is-active': editor.isActive('strike') }"
>
删除线
</button>
<button
@click="editor.chain().focus().toggleBulletList().run()"
:class="{ 'is-active': editor.isActive('bulletList') }"
>
无序列表
</button>
<button
@click="editor.chain().focus().toggleOrderedList().run()"
:class="{ 'is-active': editor.isActive('orderedList') }"
>
有序列表
</button>
<button
@click="editor.chain().focus().toggleBlockquote().run()"
:class="{ 'is-active': editor.isActive('blockquote') }"
>
引用
</button>
<button @click="editor.chain().focus().setHorizontalRule().run()">
水平线
</button>
<button @click="editor.chain().focus().setHardBreak().run()">
换行
</button>
<button @click="editor.chain().focus().unsetAllMarks().run()">
清除格式
</button>
</div>
<editor-content :editor="editor" />
<div class="content" v-if="editor">
<h3>编辑器内容 (JSON):</h3>
<pre>{{ editor.getJSON() }}</pre>
<h3>编辑器内容 (HTML):</h3>
<pre>{{ editor.getHTML() }}</pre>
</div>
</div>
</template>
<script>
import { Editor, EditorContent } from '@tiptap/vue-3'
import StarterKit from '@tiptap/starter-kit'
export default {
components: {
EditorContent,
},
data() {
return {
editor: null,
}
},
mounted() {
this.editor = new Editor({
content: `
<h2>欢迎使用 Tiptap!</h2>
<p>这是一个功能丰富的富文本编辑器,基于 Vue.js 和 ProseMirror 构建。</p>
<p>尝试使用上面的菜单来格式化文本,或者输入 <code>/</code> 来查看斜杠命令。</p>
`,
extensions: [
StarterKit,
],
onUpdate: () => {
// 编辑器内容更新时触发
// 可以在这里添加保存逻辑
},
})
},
beforeUnmount() {
this.editor.destroy()
},
}
</script>
<style>
.editor {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.menu-bar {
display: flex;
flex-wrap: wrap;
gap: 5px;
margin-bottom: 10px;
padding-bottom: 10px;
border-bottom: 1px solid #eee;
}
.menu-bar button {
padding: 5px 10px;
background: #f5f5f5;
border: 1px solid #ddd;
border-radius: 3px;
cursor: pointer;
}
.menu-bar button:hover {
background: #eee;
}
.menu-bar button.is-active {
background: #4a90e2;
color: white;
border-color: #4a90e2;
}
.ProseMirror {
min-height: 200px;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
}
.ProseMirror:focus {
outline: none;
border-color: #4a90e2;
}
.content {
margin-top: 20px;
padding: 10px;
background: #f9f9f9;
border-radius: 4px;
}
.content pre {
white-space: pre-wrap;
background: #f0f0f0;
padding: 10px;
border-radius: 4px;
}
</style>