Slate
完全可定制的富文本编辑框架,以模块化和灵活的架构著称。
Slate 是完全可定制的富文本编辑框架,提供原语而非预设,基于嵌套 JSON 节点树数据模型,所有编辑器行为通过插件实现,被 Discord、Grafana、GitBook 等产品采用,极致可定制性是其最大优势。
Slate
完全可定制的富文本编辑框架,以模块化和灵活的架构著称。
技术简介说明
Slate 是由 Ian Storm Taylor 创建的可定制富文本编辑框架。它的核心设计哲学是"提供原语而非预设"——Slate 不提供任何开箱即用的功能(包括加粗、列表等),而是给开发者提供构建任何编辑器体验所需的全部工具。
Slate 的数据模型基于嵌套的 JSON 节点树,每个节点可以是 Element(容器)、Text(叶子节点)或 Editor(根节点)。这种简洁而强大的模型使得开发者可以定义任意复杂的文档结构,同时保持数据序列化/反序列化的简单性。
自 2016 年发布以来,Slate 被 Discord、Grafana、GitBook、Taskade 等知名产品采用。其最大的优势在于极致的可定制性——没有任何编辑器行为是硬编码的,所有功能都通过插件和自定义渲染实现。2024–2026 年间,Slate 持续迭代至 v0.124+,同时其上层框架 Plate.js 已成为最活跃的 Slate 生态项目。
基本信息
- 官网:https://www.slatejs.org/
- GitHub:https://github.com/ianstormtaylor/slate
- License:MIT
- 最新版本:slate 0.124.1 / slate-react 0.125.1(2026 年中)
- 主要维护者:Ian Storm Taylor(个人项目,社区驱动)
- GitHub Stars:~31,600+
- npm 周下载量:~2.5M(slate 核心包)
- 框架支持:React(主要)/ 通用(核心层)
快速上手
安装
# 核心包
npm install slate slate-react slate-history
# slate-dom 是 slate-react 的 DOM 层(2024 年拆分)
npm install slate-dom
# TypeScript 类型(已内置)
# slate 从 0.65+ 开始内置 TypeScript 类型基础配置
import { createEditor, Descendant } from 'slate'
import { Slate, Editable, withReact } from 'slate-react'
import { withHistory } from 'slate-history'
// 初始文档内容
const initialValue: Descendant[] = [
{
type: 'paragraph',
children: [{ text: 'Hello World!' }],
},
]
// 创建编辑器(组合增强函数)
const editor = withHistory(withReact(createEditor()))最小示例(React)
import React, { useMemo, useState } from 'react'
import { createEditor, Descendant } from 'slate'
import { Slate, Editable, withReact } from 'slate-react'
import { withHistory } from 'slate-history'
const initialValue: Descendant[] = [
{ type: 'paragraph', children: [{ text: 'A line of text in a paragraph.' }] },
]
export default function App() {
const editor = useMemo(() => withHistory(withReact(createEditor())), [])
const [value, setValue] = useState<Descendant[]>(initialValue)
return (
<Slate editor={editor} initialValue={value} onChange={setValue}>
<Editable
placeholder="开始编辑..."
style={{
minHeight: '200px',
padding: '10px',
border: '1px solid #ccc',
outline: 'none',
}}
/>
</Slate>
)
}自定义元素渲染
import { RenderElementProps, RenderLeafProps } from 'slate-react'
// 自定义元素渲染
const renderElement = (props: RenderElementProps) => {
switch (props.element.type) {
case 'heading-one':
return <h1 {...props.attributes}>{props.children}</h1>
case 'heading-two':
return <h2 {...props.attributes}>{props.children}</h2>
case 'quote':
return <blockquote {...props.attributes}>{props.children}</blockquote>
default:
return <p {...props.attributes}>{props.children}</p>
}
}
// 自定义叶子节点渲染(加粗、斜体等)
const renderLeaf = (props: RenderLeafProps) => {
let { children } = props
if (props.leaf.bold) children = <strong>{children}</strong>
if (props.leaf.italic) children = <em>{children}</em>
if (props.leaf.underline) children = <u>{children}</u>
if (props.leaf.code) children = <code>{children}</code>
return <span {...props.attributes}>{children}</span>
}
// 使用
<Editable renderElement={renderElement} renderLeaf={renderLeaf} />核心概念与架构
架构设计
Slate 的架构基于以下核心原则:
- 一等公民的 JSON 模型:文档是纯 JSON,易于序列化和传输
- 框架无关的核心:
slate核心包不包含任何 DOM 代码 - 插件即增强函数:插件是接收编辑器并返回增强编辑器的函数
- 嵌套文档模型:支持任意深度的节点嵌套
- 不可变操作:通过 Operation 层实现可预测的状态变更
核心概念
- Editor:根节点,同时也是编辑器实例,包含所有插件方法
- Element:包含子节点的容器节点,可以是 block 或 inline
- Text:叶子节点,包含文本内容和格式标记(marks)
- Node:Editor、Element、Text 的统称
- Path:定位节点的数字数组路径,如
[0, 1, 2] - Point:Path + offset,精确定位到文本中的某个字符位置
- Range:两个 Point 组成的范围(锚点 anchor + 焦点 focus)
- Location:Path、Point 或 Range 的统称
- Operation:8 种原子操作(insert_text、remove_text、insert_node 等)
- Transform:由一组 Operation 组成的高级操作
插件模型
Slate 的插件是高阶函数(Higher-Order Functions):
import { Editor } from 'slate'
const withImages = (editor: Editor) => {
const { isVoid, insertData } = editor
editor.isVoid = (element) => {
return element.type === 'image' ? true : isVoid(element)
}
editor.insertData = (data) => {
const files = data.files
if (files && files.length > 0) {
// 处理图片粘贴
for (const file of files) {
const [mime] = file.type.split('/')
if (mime === 'image') {
const url = URL.createObjectURL(file)
insertImage(editor, url)
}
}
} else {
insertData(data)
}
}
return editor
}
// 使用
const editor = withImages(withHistory(withReact(createEditor())))核心特性
- 极致可定制:所有编辑器行为均可通过插件和渲染函数覆盖
- 嵌套文档模型:支持任意深度的节点嵌套结构
- JSON 序列化:文档是纯 JSON,易于存储、传输和版本控制
- 插件即函数:简单而强大的插件模型,无黑盒
- 框架分离:核心层与 DOM/React 层分离(slate-dom 包)
- Redux 风格选择器:slate-react 的选择器防止不必要的重渲染
- 撤销/重做:通过 slate-history 提供完整的操作历史
- 协作编辑:通过 slate-yjs 与 Yjs 集成实现实时协作
- Scrubber 接口:新特性,允许遮蔽异常文本中的用户数据
- TypeScript 支持:完整的类型定义
生态图
核心包
| 包名 | 说明 |
|---|---|
slate | 核心模型和编辑器逻辑(框架无关) |
slate-react | React 组件和 hooks |
slate-dom | DOM 层(从 slate-react 拆分) |
slate-history | 撤销/重做功能 |
slate-hyperscript | JSX 测试支持 |
上层框架生态
- Plate.js:最活跃的 Slate 上层框架,提供 40+ 插件和 shadcn/ui 组件
- 最新版本:v52+(2025–2026)
- AI 集成、MCP 支持
- 开箱即用的模板和组件
协作编辑
- slate-yjs:Yjs 与 Slate 的桥接层
- Liveblocks:提供 Slate 协作集成方案
社区资源
- Slate Builder:可视化 Slate 编辑器构建工具
- Slate 插件列表:https://github.com/ianstormtaylor/slate/wiki/Plugin-List
适用场景
- 高度定制的编辑器:需要完全控制编辑器的每个行为(如 Discord 的消息编辑器)
- 非标准编辑体验:如可拖拽的块编辑器、代码+富文本混合编辑器
- React 项目:Slate 的 React 集成最成熟(slate-react)
- 数据驱动的编辑器:利用 JSON 模型实现文档的程序化操作
- 实时协作编辑器:通过 slate-yjs 或 Liveblocks 集成
- 复杂文档结构:需要深度嵌套的文档模型(如表格中的表格)
- CMS 内容编辑:与 Headless CMS 集成的内容编辑场景
开发与工程化
开发流程
# 克隆仓库
git clone https://github.com/ianstormtaylor/slate.git
cd slate
# 安装依赖(使用 yarn workspaces)
yarn install
# 构建
yarn build
# 测试
yarn test
# 运行示例
yarn dev自定义插件开发模式
// 典型的插件开发模式
const withMyFeature = (editor: Editor) => {
// 1. 覆盖编辑器方法
const { normalizeNode, isVoid, isInline } = editor
// 2. 添加自定义逻辑
editor.normalizeNode = (entry) => {
// 自定义规范化规则
normalizeNode(entry) // 记得调用原始方法
}
// 3. 添加新方法
editor.insertMention = (character: string) => {
// ...
}
return editor
}测试策略
// 使用 slate-hyperscript 创建测试文档
/** @jsx jsx */
import { jsx } from 'slate-hyperscript'
const editor = (
<editor>
<element type="paragraph">
<text>Hello <text bold>World</text></text>
</element>
</editor>
)性能与安全
性能特点
- Redux 风格选择器:slate-react 的选择器可以阻止每次按键时的全量重渲染
- 增量更新:DOM 更新基于 diff,仅修改变化的部分
- 大文档挑战:Slate 默认不做虚拟滚动,大文档需要额外优化
性能优化
- 使用
useSlateSelector避免不必要的组件重渲染 - 对大文档考虑虚拟化渲染(如 react-virtuoso)
- 合理使用
React.memo包裹自定义元素组件 - 避免在
onChange中执行重计算
安全注意
- 渲染自定义元素时注意 XSS 防护
- 对用户粘贴的 HTML 内容进行消毒
- 服务端序列化时避免执行用户提供的脚本
- 使用 DOMPurify 处理编辑器输出
技术对比
| 特性 | Slate | TipTap | Lexical | Quill | ProseMirror |
|---|---|---|---|---|---|
| 架构 | 无头框架 | 无头框架 | 无头框架 | 传统 WYSIWYG | 底层框架 |
| 框架 | React 为主 | React/Vue/Svelte | React 为主 | 框架无关 | 框架无关 |
| 开箱即用 | ⚠️ 极少 | ✅ 中等 | ⚠️ 较少 | ✅ 较多 | ❌ 极少 |
| 文档模型 | JSON 嵌套树 | ProseMirror 模型 | 层级节点 | Delta/Blot | ProseMirror 模型 |
| 插件模型 | 高阶函数 | 扩展系统 | Command/Plugin | Module | Plugin |
| 协作编辑 | slate-yjs | y-tiptap(付费) | 内置(有限) | 需自建 | prosemirror-collab |
| 纯装饰 | ✅ | ✅ | ❌ | ❌ | ✅ |
| 1.0 版本 | ❌ 仍在 beta | ✅ 3.0 稳定 | ❌ 0.x | ✅ 2.0 | ✅ 稳定 |
| 学习曲线 | 较陡 | 中等 | 较陡 | 低 | 陡峭 |
最佳实践
- 考虑使用 Plate.js:除非需要极致的底层控制,Plate.js 提供了一站式解决方案
- TypeScript 类型扩展:使用模块增强扩展内置类型
import { BaseElement } from 'slate' declare module 'slate' { interface CustomTypes { Element: BaseElement & { type: 'paragraph' | 'heading' } } } - 插件组合顺序重要:
withHistory(withReact(createEditor()))的顺序会影响行为 - 规范化(Normalization)是关键:理解 Slate 的规范化机制是正确使用 Slate 的关键
- 避免直接修改 Editor 对象:使用
TransformsAPI 进行文档操作 - 协作编辑从规划开始:如果需要协作,在数据模型设计阶段就考虑 CRDT 兼容性
- 外部状态更新:通过
Editor.insertText等 Transform 方法而非直接修改 value
技术局限与边界
- 仍在 Beta:发布多年但至今未发布 1.0,API 可能仍有变动
- React 绑定:核心虽框架无关,但实际使用几乎绑定 React(无官方 Vue/Angular 支持)
- 缺少内置功能:没有内置的加粗、列表等功能,一切从底层开始构建
- Android IME 问题:Android 输入法的兼容性历史上存在较多问题
- 大文档性能:默认无虚拟滚动,需要自行优化
- 迁移成本:从 Slate 迁移到其他编辑器(或反向迁移)需要处理数据模型差异
- 社区活跃度波动:核心维护者投入不稳定,社区有时质疑项目是否"dead"
- Plate.js 生态分裂:Plate 的快速发展可能导致 Slate 核心被忽视
学习资源
- 官方文档:https://docs.slatejs.org/
- 示例集:https://docs.slatejs.org/walkthroughs
- API 文档:https://docs.slatejs.org/api
- GitHub 仓库:https://github.com/ianstormtaylor/slate
- Changelog:https://docs.slatejs.org/general/changelog
- Plate.js 文档:https://platejs.org/docs
- 社区教程:搜索 "Slate.js tutorial" 有大量优质资源
2026 年现状
最新版本
- slate:0.124.1(~2026 年 4 月)
- slate-react:0.125.1(~2026 年 7 月)
- slate-dom:独立包(从 slate-react 拆分,~2024–2025)
发展趋势
- slate-dom 拆分:DOM 层独立为 slate-dom 包,为非 React 框架适配铺路
- Plate.js 主导:Plate.js(v52+)已成为 Slate 生态最活跃的项目
- AI 集成、MCP 支持、shadcn/ui 组件
- Git 兼容的版本控制
- Redux 风格选择器:新增选择器机制防止每次按键的重渲染
- Scrubber 接口:新增数据遮蔽接口用于隐私保护
- 发布流程改进:采用 changesets 进行结构化版本管理
社区活跃度
- GitHub Stars:~31,600+
- npm 周下载量:~2.5M
- 1.0 版本 milestone 存在但无明确日期(11/20 issues 已完成)
- 社区贡献持续,PR 活跃(#5937 等持续有新 PR)
定位总结
Slate 在 2026 年依然是最灵活的可定制编辑框架,但需要注意其仍为 Beta 状态且以 React 为主。对于新项目,如果不需要极致的底层控制,建议评估 Plate.js 作为起点。如果需要跨框架支持(Vue/Angular),TipTap 可能是更好的选择。