主要介绍了编辑器的基础使用,包括依赖安装、基础配置,以及 StarterKit 基础扩展、内嵌图片 Image、编辑状态、拖拽手柄、文本样式扩展包等等
基础介绍
ProseMirror:是一款用于在网页端构建富文本编辑器的工具包,核心目标是弥合 "结构化内容编辑"(如 Markdown、XML)与 "经典所见即所得(WYSIWYG)编辑" 之间的差距 ------ 既让用户能以直观的 WYSIWYG 方式编辑,又能生成干净、语义明确且符合自定义结构的文档,适用于从简单文本编辑到复杂协作系统的各类场景。
Tiptap 是基于 ProseMirror(经行业验证的网页富文本编辑器工具库)构建的无头(Headless)富文本编辑器框架,核心能力是帮助开发者打造 "完全贴合自身及用户需求" 的定制化编辑器。
其底层依赖三大核心机制实现灵活且强大的编辑功能 API:
- 事件(Events):监听编辑器状态变化(如内容修改、光标移动);
- 命令(Commands):触发编辑操作(如加粗文本、插入列表);
- 扩展(Extensions):扩展编辑器功能(如添加表格、AI 辅助创作)。
核心价值是 "按需构建编辑器",既提供基础开源编辑功能,也通过云服务和扩展满足复杂场景需求,核心能力分为五大模块,覆盖从基础编辑到高级协作的全场景:Editor(编辑器)、Collaboration(协作)、Content AI(内容 AI)、Comments(评论)、Documents(文档处理)
安装
@tiptap/react:Tiptap 的 React 绑定包,包含核心功能(如 useEditor 钩子、EditorContent 组件)@tiptap/pm:Tiptap 依赖的 ProseMirror 底层库,是编辑器运行的核心支撑@tiptap/starter-kit:基础扩展集合,包含段落、标题、加粗、斜体等常用功能,可快速启动项目
bash
pnpm install @tiptap/react @tiptap/pm @tiptap/starter-kit
基础使用
参照官方文档,完成了一个初始 Demo
jsx
import { Image } from "antd";
import { styled } from "styled-components";
import { EditorContent, EditorContext, useEditor } from "@tiptap/react";
import { BubbleMenu, FloatingMenu } from "@tiptap/react/menus"; // 导入菜单组件
import StarterKit from "@tiptap/starter-kit"; // 导入基础扩展集合
import { useMemo } from "react";
export default function Page() {
// 1. 使用 useEditor 钩子初始化编辑器
const editor = useEditor({
// 配置扩展(此处使用基础扩展集合)
extensions: [StarterKit],
// 编辑器初始内容(HTML 格式)
content: `
<h1>第一章原始阶段和商周时期的江南</h1>
<p>地方特点的青铜器。第一类典型的商代青铜器特别是那些有铭文的铜器,很可能是从中原地区传过来的;第三类具有地方特点的青铜器应是在本地铸造的;第二类青铜器有的可能铸自本地,也有的可能来自中原。</p>
<p>这些商代铜器除了少数出自遗址和墓葬外,绝大多数出自窖藏,且多出自山顶、山腰、河岸、湖边,很有可能是当时人们祭祀山川、湖泊、日月星辰的遗物。①</p>
<p>宁乡、湘潭等地出土的商代铜器中,有"己"分裆鼎、"癸母"提梁卤和"戈"卤等少量有铭文的铜器②;在湘潭县青山桥一铜器窖穴中也出土有商末周初的"母"爵,"母"解和"戈"解③。"母"和"戈"本是中原商代铜器中两个常见的族徽,现在江南地区也有铸这两个族徽的铜器出土,说明在商代晚期或末期,他们中的一支曾南迁到了湘中地区。</p>
<p>湖南境内出土的西周铜器,仍以湘水流域为多,如湘潭、湘乡、浏阳、株洲、望城、衡阳、耒阳、资兴等地都有出土。器形以乐器饶、甬钟和镈为主④,还有湘潭青山桥窖藏出土的爵、解、鼎、凹字形锄等⑤。桃江连河冲出有马簋。⑥</p>
<p>西周时期的铜饶是紧接着商代晚期的大饶发展而来。商末周初的铜铙为乳钉铙,钲的每面有18个乳钉。这些乳钉的出现可能有两个来源,其乳钉的排列和数量应来源于商代云纹铙上云纹的尾部的上翘,这有江西新干商代大墓中出土的云纹铜饶为证。乳钉的形状应是对于象纹大饶钲边乳钉的承袭。乳钉铙上的乳钉不断升高,在西周初演变为尖锥状和</p>
`,
});
// 缓存 Context 值,避免不必要的重渲染
const providerValue = useMemo(() => ({ editor }), [editor]);
return (
<Container>
<div className="block-wrap">
<div className="left">
<Image src="/pdf/1-1.jpg" />
</div>
<div className="right">
{/* 提供 EditorContext,让子组件可访问编辑器实例 */}
<EditorContext.Provider value={providerValue}>
{/* 2. 编辑器内容区域:渲染编辑界面 */}
<EditorContent editor={editor} />
{/* 3. 浮动菜单:空行光标定位时显示 */}
<FloatingMenu editor={editor}>这是浮动菜单</FloatingMenu>
{/* 4. 气泡菜单:选中文本时显示 */}
<BubbleMenu editor={editor}>这是气泡菜单</BubbleMenu>
</EditorContext.Provider>
</div>
</div>
</Container>
);
}
const Container = styled.div`
.block-wrap {
display: flex;
width: 80%;
margin: 0 auto;
> div {
width: 50%;
}
}
`;
显示效果如图 tiptap-1-1 所示:

StarterKit 基础扩展集合
StarterKit 默认包含的核心扩展:文档(Document)、段落(Paragraph)、文本(Text)、标题(Heading)、加粗(Bold)、斜体(Italic)、删除线(Strike)、代码(Code)、无序列表(BulletList)、有序列表(OrderedList)等等。
具体入门套件,可以查看官网:StarterKit
在 StarterKit.configure() 中传入配置对象,通过 "扩展名称" 精准定位需修改的模块
jsx
import Strike from "@tiptap/extension-strike"; // 导入删除线扩展
new Editor({
extensions: [
StarterKit.configure({
heading: { levels: [1, 2, 3] }, // 调整标题层级
}),
],
});
内嵌图片 Image
具体可查看官方文档:image
bash
# 安装扩展
pnpm install @tiptap/extension-image
使用示例如下所示:
jsx
import { Image as TiptapImage } from "@tiptap/extension-image"; // 图片扩展
const editor = useEditor({
// 配置扩展(此处使用基础扩展集合)
extensions: [
StarterKit.configure({
heading: { levels: [1, 2, 3] }, // 调整标题层级
}),
TiptapImage.configure({
inline: true, // 允许行内
allowBase64: true, // 允许 base64 格式图片
HTMLAttributes: {
class: "tiptap-img-inline", // 自定义样式名
},
}),
],
content: `
<p>宁乡、湘潭等地出土的商代铜器中,有"己<img src="/pdf/1-1-1.png" />"分裆鼎、"癸<img src="https://placehold.co/40x40/6A00F5/white" />"提梁卤和"戈"卤等少量有铭文的铜器②;在湘潭县青山桥一铜器窖穴中也出土有商末周初的"<img src="/pdf/1-1-2.png" />"爵,"<img src="/pdf/1-1-2.png" />"解和"戈"解③。"<img src="/pdf/1-1-2.png" />"和"戈"本是中原商代铜器中两个常见的族徽,现在江南地区也有铸这两个族徽的铜器出土,说明在商代晚期或末期,他们中的一支曾南迁到了湘中地区。</p>
`,
});
配置了inline: true,设置为行内图片,但是没起作用呀。
排查发现,有个默认样式配置,将图片设置为块元素了,那只能自定义样式处理了呗。

只能添加自定义样式HTMLAttributes: {class:"tiptap-img-inline"}
css
.tiptap-img-inline {
display: inline; /* 强制行内显示 */
}

当然,如果不想要行内显示的话,就不将 img 放到 p 标签内,而是直接放在 p 标签外,这样图片就会块级元素了。
代码展示:

渲染效果展示:

编辑状态控制
jsx
// 控制编辑器可编辑状态
const [isEditable, setIsEditable] = useState(false);
useEffect(() => {
if (editor) {
editor.setEditable(isEditable);
}
}, [isEditable, editor]);
// 开关渲染
<Switch
checkedChildren="开启"
unCheckedChildren="关闭"
checked={isEditable}
onChange={setIsEditable}
/>;

拖拽手柄控制
官方文档:drag-handle-react
文本样式集合包
文本样式集合包含:TextStyle、BackgroundColor(背景色)、Color(颜色)、FontFamily(字体)、FontSize(字号)、LineHeight(行高)
官方文档:text-style-kit
bash
# 安装插件
pnpm install @tiptap/extension-text-style
jsx
import { TextStyleKit } from "@tiptap/extension-text-style"; // 文本样式扩展
const editor = useEditor({
extensions: [
StarterKit,
// 添加文本样式功能集合,包含字体、颜色、背景色、行高、字体大小等样式控制
TextStyleKit,
TiptapImage,
],
});