这篇文章讨论怎么定制 Draft 的 block 渲染。Block 渲染用于定义支持的 block 类型和它们的渲染器,以及将解析之后的的内容转换为已知的 Draft block 类型。

当解析内容或者使用 convertFromHTML 时,Draft 会将标签和 Draft block 进行匹配,将解析后的内容转化为已知的 block 渲染类型。

默认的 block 渲染表

HTML 标签 Draft block 类型
<h1/> header-one
<h2/> header-two
<h3/> header-three
<h4/> header-four
<h5/> header-five
<h6/> header-six
<blockquote/> blockquote
<pre/> code-block
<figure/> atomic
<li/> unordered-list-item,ordered-list-item**
<div/> unstyled*

- Block 类型建立在父级元素

      的基础上。
      *
      - 任何不在上边列表的block类型都会被当做 unstyled

      配置 block 渲染列表

      Draft 默认的 block 渲染可以通过给 Editor 组件传入类型为 Immutable MapblockRender prop 来重写。

      例如改写默认的 block 渲染。

      1. // The example below deliberately only allows
      2. // 'heading-two' as the only valid block type and
      3. // updates the unstyled element to also become a h2.
      4. const blockRenderMap = Immutable.Map({
      5. 'header-two': {
      6. element: 'h2'
      7. },
      8. 'unstyled': {
      9. element: 'h2'
      10. }
      11. });
      12. class RichEditor extends React.Component {
      13. render() {
      14. return (
      15. <Editor
      16. ...
      17. blockRenderMap={blockRenderMap}
      18. />
      19. );
      20. }
      21. }

      除了重写 block 类型,我们还能够添加自定义的 block 类型。通过 DefaultDraftBlockRenderMap 来新建一个blockRenderMap。

      扩展默认的 block render map。

      const blockRenderMap = Immutable.Map({
        'section': {
          element: 'section'
        }
      });
      // Include 'paragraph' as a valid block and updated the unstyled element but
      // keep support for other draft default block types
      const extendedBlockRenderMap = Draft.DefaultDraftBlockRenderMap.merge(blockRenderMap);
      class RichEditor extends React.Component {
        render() {
          return (
            <Editor
              ...
              blockRenderMap={extendedBlockRenderMap}
            />
          );
        }
      }
      

      当 Draft 解析一个已经被解析的 html,它将 html 元素转化为 Draft block 类型。如果你想让其它的标签也能转化为指定的 block 类型,你可以在 block config 中添加 aliasedElements

      unstyled block 类型别名的使用:

      'unstyled': {
        element: 'div',
        aliasedElements: ['p'],
      }
      

      自定义 block wrappers

      默认的情况下,html 标签被用来包裹 block 类型。通过 blockRenderMap,React 组件也能够用来包裹EditorBlock。

      在解析或者使用 convertFromHTML 时 html 会被扫描,来匹配特定的标签。一个 wrapper 会用来承载特定的block 类型。例如:

      Draft 使用 <ol/> 或者 <ul/> 来包裹 <li/> 。 wrapper 也可以在其它类型的 block上使用。

      在自定义 block 类型上使用 React 组件作为 wrapper 的例子:

      class MyCustomBlock extends React.Component {
        constructor(props) {
          super(props);
        }
        render() {
          return (
            <div className='MyCustomBlock'>
              {/* here, this.props.children contains a <section> container, as that was the matching element */}
              {this.props.children}
            </div>
          );
        }
      }
      const blockRenderMap = Immutable.Map({
        'MyCustomBlock': {
          // element is used during paste or html conversion to auto match your component;
          // it is also retained as part of this.props.children and not stripped out
          element: 'section',
          wrapper: <MyCustomBlock />,
        }
      });
      // keep support for other draft default block types and add our myCustomBlock type
      const extendedBlockRenderMap = Draft.DefaultDraftBlockRenderMap.merge(blockRenderMap);
      class RichEditor extends React.Component {
        ...
        render() {
          return (
            <Editor
              ...
              blockRenderMap={extendedBlockRenderMap}
            />
          );
        }
      }