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

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

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

npm下载趋势

3 年

GitHub Stars 排名

统计详情

npm包名称
下载量
Stars
大小
Issues
发布时间
License
draft-js022,634-9546 年前MIT
slate031,7122.17 MB6623 个月前MIT

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

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

🚨 维护状态:官方弃用 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 和逻辑全由你定义。通过 withReactwithHistory 等插件模式增强功能。

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

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

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

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

draft-js 需要管理 EditorStateonChange 的闭环,且必须包裹在 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

如何选择: draft-js vs slate

  • draft-js:

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

  • slate:

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

draft-js的README

draftjs-logo

Draft.js

Build Status npm version

Live Demo


Draft.js is a JavaScript rich text editor framework, built for React and backed by an immutable model.

  • Extensible and Customizable: We provide the building blocks to enable the creation of a broad variety of rich text composition experiences, from basic text styles to embedded media.
  • Declarative Rich Text: Draft.js fits seamlessly into React applications, abstracting away the details of rendering, selection, and input behavior with a familiar declarative API.
  • Immutable Editor State: The Draft.js model is built with immutable-js, offering an API with functional state updates and aggressively leveraging data persistence for scalable memory usage.

Learn how to use Draft.js in your own project.

API Notice

Before getting started, please be aware that we recently changed the API of Entity storage in Draft. The latest version, v0.10.0, supports both the old and new API. Following that up will be v0.11.0 which will remove the old API. If you are interested in helping out, or tracking the progress, please follow issue 839.

Getting Started

npm install --save draft-js react react-dom

or

yarn add draft-js react react-dom

Draft.js depends on React and React DOM which must also be installed.

Using Draft.js

import React from 'react';
import ReactDOM from 'react-dom';
import {Editor, EditorState} from 'draft-js';

class MyEditor extends React.Component {
  constructor(props) {
    super(props);
    this.state = {editorState: EditorState.createEmpty()};
    this.onChange = (editorState) => this.setState({editorState});
    this.setEditor = (editor) => {
      this.editor = editor;
    };
    this.focusEditor = () => {
      if (this.editor) {
        this.editor.focus();
      }
    };
  }

  componentDidMount() {
    this.focusEditor();
  }

  render() {
    return (
      <div style={styles.editor} onClick={this.focusEditor}>
        <Editor
          ref={this.setEditor}
          editorState={this.state.editorState}
          onChange={this.onChange}
        />
      </div>
    );
  }
}

const styles = {
  editor: {
    border: '1px solid gray',
    minHeight: '6em'
  }
};

ReactDOM.render(
  <MyEditor />,
  document.getElementById('container')
);

Since the release of React 16.8, you can use Hooks as a way to work with EditorState without using a class.

import React from 'react';
import ReactDOM from 'react-dom';
import {Editor, EditorState} from 'draft-js';

function MyEditor() {
  const [editorState, setEditorState] = React.useState(
    EditorState.createEmpty()
  );

  const editor = React.useRef(null);

  function focusEditor() {
    editor.current.focus();
  }

  React.useEffect(() => {
    focusEditor()
  }, []);

  return (
    <div onClick={focusEditor}>
      <Editor
        ref={editor}
        editorState={editorState}
        onChange={editorState => setEditorState(editorState)}
      />
    </div>
  );
}

Note that the editor itself is only as tall as its contents. In order to give users a visual cue, we recommend setting a border and a minimum height via the .DraftEditor-root CSS selector, or using a wrapper div like in the above example.

Because Draft.js supports unicode, you must have the following meta tag in the <head> </head> block of your HTML file:

<meta charset="utf-8" />

Further examples of how Draft.js can be used are provided below.

Examples

Visit http://draftjs.org/ to try out a basic rich editor example.

The repository includes a variety of different editor examples to demonstrate some of the features offered by the framework.

To run the examples, first build Draft.js locally. The Draft.js build is tested with Yarn v1 only. If you're using any other package manager and something doesn't work, try using yarn v1:

git clone https://github.com/facebook/draft-js.git
cd draft-js
yarn install
yarn run build

then open the example HTML files in your browser.

Draft.js is used in production on Facebook, including status and comment inputs, Notes, and messenger.com.

Browser Support

IE / Edge
IE / Edge
Firefox
Firefox
Chrome
Chrome
Safari
Safari
iOS Safari
iOS Safari
Chrome for Android
Chrome for Android
IE11, Edge [1, 2]last 2 versionslast 2 versionslast 2 versionsnot fully supported [3]not fully supported [3]

[1] May need a shim or a polyfill for some syntax used in Draft.js (docs).

[2] IME inputs have known issues in these browsers, especially Korean (docs).

[3] There are known issues with mobile browsers, especially on Android (docs).

Resources and Ecosystem

Check out this curated list of articles and open-sourced projects/utilities: Awesome Draft-JS.

Discussion and Support

Join our Slack team!

Contribute

We actively welcome pull requests. Learn how to contribute.

License

Draft.js is MIT licensed.

Examples provided in this repository and in the documentation are separately licensed.