learn draft-js

- 3 mins

what is draft-js

draft-js 是一个基于 reactimmutable-js 的富文本编辑器, 可以方便的集成到 react 代码中。

富文本编辑器要解决的问题

编辑器最终要解决的是两个方面问题:

输入

  1. 同步 stateinput 的值
  2. 监听 inputonChange 事件

富文本

  1. 单纯的文本不足以满足复杂的形式
  2. 可编辑的元素没有 onChange 事件

富文本内容

富文本的内容可以分为以下几个部分

它们之间的大致关系可以粗浅的立即为下图(箭头为包含关系)

箭头表示包含关系,函数名为可返回箭头右侧实例的方法

EditorState

为了解决以上两方面的问题,draft-js 引入了StateonChange。 对于熟悉 react的工程师来说,这两个概念已经不是新鲜的概念了。

其中State作为所有交互和输入数据的状态,被包装成了一个不可变的数据类型EditorState

EditorState 的数据类型依托于

Record,其中包括了以下几类数据,用于描述draft-js 编辑器中的所有状态

  1. 当前文本内容, current text content state
  2. 当前选中内容, current selection state
  3. 内容装饰, decorated representation of the contents
  4. 操作记录, Undo/redo stacks
  5. 最近的内容变化的类型, The most recent type of change made to the contents

整个editorState 就是要编辑的所有状态,所以最好是将所有状态的修改都通过 editorState 提供的 Api 来进行,而不要通过 immutable-js 的 Api 来进行.


ContentState

ContentState可以理解为内容的状态,所有对内容的更新,本质上都是更新这个状态的实例


ContentBlock

ContentBlock是在 ContentState 中实际描述内容的单元,每个 ContentBlock 实例包括了以下几部分:

每个 ContentBlock都有其唯一的 key,可以通过 key 来指定需要操作的 ContentBlock

Decorators

ContentBlock中,有时会对一些文本进行特殊展示,这种情况下可以使用 Decorator来对其进行装饰,使其在编辑器中展示出特别的样式和 html 结构。

每一个 Decorator 对象都有以下这两个属性:

strategy: handleStrategy (contentBlock, callback, contentState) => {},
component: HandleSpan (props) => {}

看一个具体的例子,下面的这段代码匹配到了 @# 并返回了富文本


// strategy
const HANDLE_REGEX = /\@[\w]+/g;
const HASHTAG_REGEX = /\#[\w\u0590-\u05ff]+/g;

function handleStrategy(contentBlock, callback, contentState) {
  findWithRegex(HANDLE_REGEX, contentBlock, callback);
}

function hashtagStrategy(contentBlock, callback, contentState) {
  findWithRegex(HASHTAG_REGEX, contentBlock, callback);
}

function findWithRegex(regex, contentBlock, callback) {
  const text = contentBlock.getText();
  let matchArr, start;
  while ((matchArr = regex.exec(text)) !== null) {
    start = matchArr.index;
    callback(start, start + matchArr[0].length);
  }
}

// component
const HandleSpan = (props) => {
  return <span {...props} style={styles.handle}>{props.children}</span>;
};

const HashtagSpan = (props) => {
  return <span {...props} style={styles.hashtag}>{props.children}</span>;
};

这里需要注意的是 callback 方法实际接收的是匹配到的文本内容的起始位置和结束位置,也可以称为 range

另外,Decorator 需要通过

EditorState.set(editorState, {decorator: decorators})

的方式来添加到 editorState 中,这样才可以正常使用。


Entity

entity 是一个最小单元的富文本对象,每一个 entity 对象都有三个属性:

entity 对象用于描述 contentState 中的selectionState, 举个例子,这是一个**创建 entity -> 获取 entityKey -> 使用 Modifier 将 entity 应用到 selectionState **

const contentState = editorState.getCurrentContent();
const contentStateWithEntity = contentState.createEntity(
  'LINK',
  'MUTABLE',
  {url: 'http://www.zombo.com'}
);
const entityKey = contentStateWithEntity.getLastCreatedEntityKey();
const contentStateWithLink = Modifier.applyEntity(
  contentStateWithEntity,
  selectionState,
  entityKey
);
tips:

重要的事情再强调一遍,Entity 中的 mutabilityImmutable Record 对象不是一回事儿,这个属性仅仅是标记在 editor 中当前的entity 是否会改变。

这个属性有三个可选择的值,类型都是字符串:

SelectionState

顾名思义,SelectionState就是 Selection 在编辑器中的状态;通过SelectionState可以获取到与 Selection 相关的ContentBlock


除了表示内容的数据结构外,draft-js 还提供了一些其他的 Utils 供开发者使用

rss facebook twitter github youtube mail spotify lastfm instagram linkedin google google-plus pinterest medium vimeo stackoverflow reddit quora