slate vs draft-js
React 富文本编辑器架构选型:Draft.js 与 Slate 深度对比
slatedraft-js类似的npm包:

React 富文本编辑器架构选型:Draft.js 与 Slate 深度对比

draft-js 和 slate 都是基于 React 构建的富文本编辑器框架,旨在帮助开发者在 Web 应用中实现复杂的文本编辑功能。draft-js 由 Facebook 开源,曾广泛使用,采用不可变数据(Immutable.js)管理状态,但目前处于维护模式,不再推荐用于新项目。slate 则是一个完全可定制的框架,没有内置 UI,依赖纯 JSON 数据模型,允许开发者构建任意类型的编辑器(如 Notion 风格的块级编辑器),目前社区活跃且持续更新。

npm下载趋势

3 年

GitHub Stars 排名

统计详情

npm包名称
下载量
Stars
大小
Issues
发布时间
License
slate4,136,66331,7472.62 MB74 天前MIT
draft-js1,297,98522,563-9546 年前MIT

Draft.js vs Slate: 架构、维护状态与扩展性深度对比

在 React 生态中,draft-js 和 slate 都曾是实现富文本编辑的热门选择。然而,随着技术演进,两者的命运截然不同。本文将从架构设计、数据模型、扩展性及维护状态四个维度,为资深开发者提供决策依据。

🚨 维护状态:官方弃用 vs 社区活跃

draft-js 目前处于维护模式。Meta 团队已明确表示不再添加新功能,仅修复严重 Bug。这意味着长期项目面临技术债务风险。

// draft-js: 官方已不再推荐用于新项目
// npm install draft-js
// 警告:依赖 Immutable.js,与现代 React 并发特性兼容性差

slate 保持活跃更新。社区驱动,版本迭代频繁,积极适配 React 新特性(如 React 18)。

// slate: 持续维护中
// npm install slate slate-react
// 支持最新的 React 渲染机制

🧱 数据模型:Immutable.js vs 纯 JSON

数据结构的差异直接影响性能优化和序列化难度。

draft-js 强制依赖 Immutable.js。这导致状态对象无法直接序列化,必须转换,且与原生 JS 对象交互繁琐。

// draft-js: 必须转换才能存储到数据库
import { convertToRaw } from 'draft-js';

const rawContent = convertToRaw(editorState.getCurrentContent());
const jsonString = JSON.stringify(rawContent); // 需要额外步骤

slate 使用纯 JSON 对象。数据即状态,可直接存储、传输,无需转换层,调试更直观。

// slate: 数据本身就是 JSON
const value = [
  {
    type: 'paragraph',
    children: [{ text: 'A plain JSON node' }]
  }
];
// 可直接 JSON.stringify(value) 存入数据库

🧩 扩展性:黑盒 vs 白盒

这是两者最核心的架构分歧。

draft-js 是电池included 的黑盒。它提供了默认的块类型、装饰器系统,但修改底层行为(如粘贴处理、光标逻辑)非常困难,常需 Hack 内部 API。

// draft-js: 自定义块类型较繁琐,需注册组件映射
const blockRendererFn = (block) => {
  if (block.getType() === 'atomic') {
    return {
      component: AtomicBlock,
      editable: false,
    };
  }
};
// 难以拦截底层键盘事件或修改选择逻辑

slate 是完全可插拔 的白盒。它只提供核心引擎,UI 和逻辑全由你定义。通过 withReact、withHistory 等插件模式增强功能。

// slate: 通过 Higher-Order Functions 增强编辑器
import { withReact } from 'slate-react';
import { withHistory } from 'slate-history';

const editor = useMemo(() => withHistory(withReact(createEditor())), []);
// 可轻松自定义快捷键、粘贴规则或渲染逻辑

✍️ 核心 API 对比:创建编辑器实例

看代码是最直接的体验差异。

draft-js 需要管理 EditorState 和 onChange 的闭环,且必须包裹在 Draft.Editor 组件中。

// draft-js: 状态管理较重
import { EditorState, RichUtils } from 'draft-js';

function MyEditor() {
  const [editorState, setEditorState] = useState(EditorState.createEmpty());
  
  const handleKeyCommand = (command, editorState) => {
    const newState = RichUtils.handleKeyCommand(editorState, command);
    if (newState) {
      setEditorState(newState);
      return 'handled';
    }
    return 'not-handled';
  };

  return (
    <Editor
      editorState={editorState}
      onChange={setEditorState}
      handleKeyCommand={handleKeyCommand}
    />
  );
}

slate 将编辑器实例与 React 状态分离,使用 Editable 组件渲染,逻辑更清晰。

// slate: 逻辑与视图分离更彻底
import { Slate, Editable, withReact } from 'slate-react';
import { createEditor } from 'slate';

function MyEditor() {
  const editor = useMemo(() => withReact(createEditor()), []);
  const [value, setValue] = useState([{ type: 'paragraph', children: [{ text: '' }] }]);

  return (
    <Slate editor={editor} initialValue={value} onChange={newValue => setValue(newValue)}>
      <Editable placeholder="Enter some rich text..." />
    </Slate>
  );
}

🛠️ 常见场景实现:加粗功能

draft-js 使用内置的 RichUtils 处理内联样式。

// draft-js: 使用预设样式
const toggleBold = () => {
  setEditorState(RichUtils.toggleInlineStyle(editorState, 'BOLD'));
};
// 样式名 'BOLD' 是硬编码的字符串

slate 需要手动操作节点属性,更灵活但代码更多。

// slate: 手动操作节点
const toggleBold = () => {
  const marks = Editor.marks(editor);
  const isBold = marks && marks.bold;
  Editor.addMark(editor, 'bold', isBold ? null : true);
};
// 可以自定义任意属性名,如 'isBold', 'weight': 700 等

📊 总结对比表

特性draft-jsslate
维护状态⚠️ 维护模式 (不推荐新项目)✅ 活跃更新
数据格式🔒 Immutable.js (需转换)📄 纯 JSON (原生支持)
定制能力📦 有限 (黑盒)🔧 极高 (白盒/插件化)
学习曲线📉 中等 (文档旧)📈 陡峭 (概念多)
包体积🐘 较大 (含 Immutable)🐦 较小 (按需引入)
适用场景🕰️ 旧项目维护🚀 复杂定制/新产品

💡 架构师建议

draft-js 就像一台老式打印机 🖨️——功能固定,坏了难修,备件(文档/社区支持)越来越少。除非你被绑定在旧系统中,否则不要在新架构中引入它。

slate 就像一套精密的机械零件 🧩——你需要自己组装,但能造出任何你想要的机器。适合需要深度控制编辑体验、数据结构复杂的场景(如在线文档、CMS 编辑器)。

最终建议:对于 2024 年及以后的新项目,首选 slate。如果 slate 的学习成本过高,可考虑基于 slate 封装的上层库(如 plate),或评估其他现代替代品(如 TipTap),但应避免使用 draft-js。

如何选择: slate vs draft-js

  • slate:

    当需要高度自定义编辑器行为、数据结构或构建类似 Notion 的块级编辑体验时,选择 slate。它适合对编辑器逻辑有完全控制权的团队,但需要接受较陡峭的学习曲线和更多的初始代码编写工作。

  • draft-js:

    仅当维护现有旧项目或需求极其简单且不需要复杂定制时选择 draft-js。由于官方已停止功能更新且依赖过时的 Immutable.js,新项目应避免使用,否则未来迁移成本极高。

slate的README

This package contains the core logic of Slate. Feel free to poke around to learn more!

Note: A number of source files contain extracted types for Interfaces or Transforms. This is done currently to enable custom type extensions as found in packages/src/interfaces/custom-types.ts.