字节笔记本

2026年2月22日

Slate:完全可定制的 React 富文本编辑器框架

Slate 是一个完全可定制的富文本编辑器框架,让你能够构建类似 Medium、Dropbox Paper 或 Google Docs 的富文本编辑器,而无需让代码库陷入复杂性。它基于 React 构建,采用插件化架构,所有逻辑都通过一系列插件实现,因此你不会被"核心"功能所限制。

项目简介

Slate 由 Ian Storm Taylor 创建,是一个开源的富文本编辑器框架。截至目前,该项目在 GitHub 上已获得 31.5k+ stars,拥有 3.3k+ forks,是一个社区驱动的项目。Slate 的设计灵感来源于 Draft.js、Prosemirror 和 Quill 等编辑器库。

注意:Slate 目前处于 Beta 阶段,核心 API 已经可用,但在高级用例中可能需要提交 PR 来改进或修复一些 bug。部分 API 尚未"最终确定",可能会随着时间推移而有破坏性变更。

为什么选择 Slate?

在创建 Slate 之前,作者尝试了 Draft.js、Prosemirror、Quill 等富文本库。虽然简单的示例很容易实现,但一旦尝试构建类似 Medium、Dropbox Paper 或 Google Docs 的编辑器,就会遇到以下深层问题:

  • 编辑器的"schema"被硬编码且难以定制 - 粗体和斜体开箱即用,但评论、嵌入或更特定领域的需求呢?
  • 程序化转换文档非常繁琐 - 用户写入可能有效,但程序化更改对于构建高级行为至关重要,却 unnecessarily 复杂
  • 序列化为 HTML、Markdown 等似乎是事后考虑 - 简单地将文档转换为 HTML 或 Markdown 涉及编写大量样板代码
  • 重新发明视图层似乎效率低下且受限 - 大多数编辑器使用自己的视图,而不是使用 React 等现有技术
  • 协同编辑没有预先设计 - 编辑器的内部数据表示通常使其无法用于实时协同编辑用例
  • 代码库是单一的,而不是小而可重用的 - 许多编辑器的代码库没有公开可以重用的内部工具
  • 构建复杂的嵌套文档是不可能的 - 许多编辑器围绕简单的"扁平"文档设计

核心设计原则

Slate 通过以下原则解决上述问题:

1. 一流的插件

插件是 Slate 最重要的部分。这意味着你可以完全自定义编辑体验,构建像 Medium 或 Dropbox 那样的复杂编辑器,而无需与库的假设作斗争。

2. 无 Schema 核心

Slate 的核心逻辑对你将编辑的数据 schema 假设很少,这意味着库中没有会绊倒你的假设。

3. 嵌套文档模型

Slate 使用的文档模型是嵌套的、递归的树,就像 DOM 本身一样。这意味着创建表格或嵌套引用等复杂组件对于高级用例是可能的。

4. 与 DOM 并行

Slate 的数据模型基于 DOM - 文档是嵌套树,使用选择和范围,并公开所有标准事件处理程序。这意味着表格或嵌套引用等高级行为是可能的。

5. 直观的命令

Slate 文档使用"命令"进行编辑,这些命令被设计为高级且极其直观,因此自定义功能尽可能具有表现力。

6. 支持协同编辑的数据模型

Slate 使用的数据模型 - 特别是操作如何应用于文档 - 已被设计为允许在顶部叠加协同编辑。

7. 清晰的"核心"边界

通过插件优先架构和无 schema 核心,"核心"和"自定义"之间的边界变得更加清晰。

技术栈

  • React - 基于 React 构建的视图层
  • TypeScript - 完全使用 TypeScript 编写
  • Lerna - Monorepo 管理工具
  • Yarn Workspaces - 包管理

安装指南

前置要求

  • Node.js >= 18
  • React >= 18
  • TypeScript >= 5.2

安装核心包

bash
# 使用 npm
npm install slate slate-react

# 或使用 yarn
yarn add slate slate-react

# 或使用 pnpm
pnpm add slate slate-react

可选插件包

bash
# 添加历史记录(撤销/重做)支持
npm install slate-history

# 添加 Hyperscript 工具(用于测试)
npm install slate-hyperscript

快速开始

以下是一个最基本的 Slate 编辑器示例:

tsx
import React, { useState, useCallback } from 'react'
import { createEditor, Descendant } from 'slate'
import { Slate, Editable, withReact } from 'slate-react'

const App = () => {
  const [editor] = useState(() => withReact(createEditor()))
  const [value, setValue] = useState<Descendant[]>([
    {
      type: 'paragraph',
      children: [{ text: 'Hello Slate!' }],
    },
  ])

  const renderElement = useCallback((props) => {
    switch (props.element.type) {
      case 'code':
        return <CodeElement {...props} />
      default:
        return <DefaultElement {...props} />
    }
  }, [])

  return (
    <Slate editor={editor} initialValue={value} onChange={setValue}>
      <Editable renderElement={renderElement} />
    </Slate>
  )
}

const CodeElement = (props) => {
  return (
    <pre {...props.attributes}>
      <code>{props.children}</code>
    </pre>
  )
}

const DefaultElement = (props) => {
  return <p {...props.attributes}>{props.children}</p>
}

核心包介绍

Slate 的代码库是一个 Monorepo,包含以下核心包:

包名描述大小
slateSlate 的核心数据模型逻辑~20KB (gzip)
slate-react用于渲染 Slate 编辑器的 React 组件~40KB (gzip)
slate-history添加撤销/重做历史记录的插件~5KB (gzip)
slate-hyperscript用于编写 JSX Slate 文档的 Hyperscript 工具~5KB (gzip)
slate-domDOM 相关工具函数~10KB (gzip)

使用示例

富文本编辑器

tsx
import { Editor, Transforms, Element as SlateElement } from 'slate'

// 切换粗体格式
const toggleBold = (editor) => {
  const isActive = isBoldActive(editor)
  Transforms.setNodes(
    editor,
    { bold: isActive ? null : true },
    { match: (n) => Text.isText(n), split: true }
  )
}

自定义元素

tsx
// 定义一个引用块元素
const QuoteElement = ({ attributes, children }) => {
  return (
    <blockquote {...attributes} style={{ borderLeft: '2px solid #ddd', paddingLeft: '10px' }}>
      {children}
    </blockquote>
  )
}

处理键盘事件

tsx
const onKeyDown = (event) => {
  if (event.key === '`' && event.ctrlKey) {
    event.preventDefault()
    // 切换代码块
    toggleCodeBlock(editor)
  }
}

官方示例

Slate 提供了丰富的官方示例:

  • Plain text - 最基本的用例:一个增强版的 <textarea>
  • Rich text - 基本编辑器功能
  • Markdown preview - 添加 Markdown 快捷键
  • Inlines - 使用内联节点包装文本
  • Images - 使用 void(无文本)节点添加图片
  • Hovering toolbar - 悬浮工具栏实现
  • Tables - 嵌套块渲染表格
  • Paste HTML - 使用 HTML 序列化处理粘贴的 HTML
  • Mentions - 使用内联 void 节点实现 @提及功能

学习资源

中文文档

注意事项

  1. Beta 阶段 - API 可能会有破坏性变更,生产环境使用前请仔细评估
  2. 社区驱动 - 没有大公司支持,所有贡献都是自愿的
  3. 需要 React 知识 - 基于 React 构建,需要一定的 React 基础
  4. 学习曲线 - 相比简单的富文本编辑器,Slate 的学习曲线较陡,但灵活性更高

相关项目

  • flomo-editor - 基于 React + Slate.js 的开源富文本编辑器

许可证

Slate 使用 MIT 许可证 开源。

分享: