5分钟上手Draft.js:从零构建React富文本编辑器,告别复杂配置

5分钟上手Draft.js:从零构建React富文本编辑器,告别复杂配置

【免费下载链接】draft-js A React framework for building text editors. 【免费下载链接】draft-js 项目地址: https://gitcode.com/gh_mirrors/dra/draft-js

你还在为React项目寻找轻量级富文本解决方案?还在为复杂的编辑器配置头痛不已?本文将带你5分钟内搭建一个功能完备的富文本编辑器,无需深入底层API,直接上手可用。读完本文,你将掌握Draft.js的核心使用方法,实现文本加粗、标题设置、列表等常用功能,并能根据需求扩展自定义样式。

什么是Draft.js?

Draft.js是Facebook开源的React富文本编辑框架(Framework),它将编辑器状态(State)与UI组件分离,通过不可变数据结构(Immutable Data Structure)高效管理编辑内容。与传统编辑器相比,Draft.js具有以下优势:

  • 深度整合React生态,支持组件化开发
  • 精确控制编辑状态,避免常见的光标跳转、内容错乱问题
  • 灵活扩展样式和功能,从简单文本框到复杂富文本编辑器均可实现

官方文档:docs/Overview.md
核心源码:src/model/immutable/EditorState.js

快速开始:环境准备

1. 安装依赖

通过npm或yarn安装Draft.js及相关依赖:

npm install draft-js react react-dom immutable --save
# 或
yarn add draft-js react react-dom immutable

2. 引入核心模块

在React组件中导入Draft.js的核心API:

import { Editor, EditorState, RichUtils } from 'draft-js';
import 'draft-js/dist/Draft.css'; // 基础样式

实现你的第一个编辑器

基础编辑器组件

以下代码将创建一个最简化的富文本编辑器,包含基础编辑功能和状态管理:

import React, { Component } from 'react';
import { Editor, EditorState } from 'draft-js';
import 'draft-js/dist/Draft.css';

class MyEditor extends Component {
  constructor(props) {
    super(props);
    // 初始化编辑器状态
    this.state = { editorState: EditorState.createEmpty() };
    this.onChange = (editorState) => this.setState({ editorState });
  }

  render() {
    return (
      <div style={{ border: '1px solid #ccc', minHeight: '100px', padding: '10px' }}>
        <Editor
          editorState={this.state.editorState}
          onChange={this.onChange}
          placeholder="开始输入..."
          spellCheck={true}
        />
      </div>
    );
  }
}

export default MyEditor;

这段代码实现了:

  • 空状态初始化:EditorState.createEmpty()
  • 状态更新机制:onChange回调函数
  • 基础样式:引入官方Draft.css
  • 占位文本和拼写检查

添加富文本控制按钮

通过RichUtils工具类,我们可以快速实现加粗、斜体等常用样式控制。以下是扩展后的编辑器组件:

class RichEditor extends Component {
  constructor(props) {
    super(props);
    this.state = { editorState: EditorState.createEmpty() };
    this.onChange = (editorState) => this.setState({ editorState });
  }

  // 切换内联样式(加粗、斜体等)
  toggleInlineStyle = (style) => {
    this.onChange(RichUtils.toggleInlineStyle(this.state.editorState, style));
  };

  render() {
    return (
      <div>
        {/* 样式控制按钮 */}
        <div style={{ marginBottom: '10px' }}>
          <button onClick={() => this.toggleInlineStyle('BOLD')}>加粗</button>
          <button onClick={() => this.toggleInlineStyle('ITALIC')}>斜体</button>
          <button onClick={() => this.toggleInlineStyle('UNDERLINE')}>下划线</button>
        </div>
        
        {/* 编辑器主体 */}
        <div style={{ border: '1px solid #ccc', minHeight: '200px', padding: '10px' }}>
          <Editor
            editorState={this.state.editorState}
            onChange={this.onChange}
            placeholder="开始编辑..."
          />
        </div>
      </div>
    );
  }
}

核心API解析:

  • RichUtils.toggleInlineStyle(editorState, style):切换内联样式,支持BOLDITALICUNDERLINE等预设样式
  • EditorState:编辑器状态对象,包含当前内容、光标位置等所有信息
  • onChange:状态变更回调,必须通过此方法更新状态才能使编辑器正常工作

进阶功能:块级样式与自定义控制

实现标题和列表功能

除了内联样式,Draft.js还支持块级样式(Block Style),如标题、列表、引用等。以下是完整示例:

// 块级样式控制组件
class BlockStyleControls extends React.Component {
  render() {
    const { editorState, onToggle } = this.props;
    const selection = editorState.getSelection();
    const blockType = editorState
      .getCurrentContent()
      .getBlockForKey(selection.getStartKey())
      .getType();

    return (
      <div>
        {[
          { label: 'H1', style: 'header-one' },
          { label: 'H2', style: 'header-two' },
          { label: 'UL', style: 'unordered-list-item' },
          { label: 'OL', style: 'ordered-list-item' },
          { label: '引用', style: 'blockquote' },
        ].map(type => (
          <button
            key={type.label}
            onClick={() => onToggle(type.style)}
            style={{ 
              margin: '0 5px',
              backgroundColor: blockType === type.style ? '#ccc' : 'white'
            }}
          >
            {type.label}
          </button>
        ))}
      </div>
    );
  }
}

// 集成块级样式的完整编辑器
class AdvancedEditor extends Component {
  state = { editorState: EditorState.createEmpty() };

  onChange = (editorState) => this.setState({ editorState });

  toggleBlockType = (blockType) => {
    this.onChange(RichUtils.toggleBlockType(this.state.editorState, blockType));
  };

  render() {
    return (
      <div>
        <BlockStyleControls 
          editorState={this.state.editorState} 
          onToggle={this.toggleBlockType} 
        />
        <Editor
          editorState={this.state.editorState}
          onChange={this.onChange}
          blockStyleFn={this.getBlockStyle}
        />
      </div>
    );
  }

  // 自定义块级样式渲染
  getBlockStyle = (block) => {
    switch (block.getType()) {
      case 'header-one': return 'font-size: 24px; font-weight: bold;';
      case 'header-two': return 'font-size: 20px; font-weight: bold;';
      case 'blockquote': return 'border-left: 4px solid #ccc; padding-left: 10px;';
      default: return null;
    }
  };
}

关键知识点:

  • RichUtils.toggleBlockType():切换块级样式
  • blockStyleFn:自定义块级样式的渲染函数
  • 块级样式类型:header-one(H1)、unordered-list-item(无序列表)等,完整列表见DraftBlockType.js

完整示例效果

结合内联样式和块级样式控制,我们可以实现类似Word的编辑体验。官方提供了完整的富文本示例,你可以直接参考:

官方示例:examples/draft-0-10-0/rich/rich.html
示例样式:examples/draft-0-10-0/rich/RichEditor.css

实战技巧:常见问题解决方案

1. 如何获取编辑器内容

通过convertToRaw方法可将编辑器内容转换为JSON格式,便于存储和传输:

import { convertToRaw } from 'draft-js';

// 获取内容JSON
const contentState = this.state.editorState.getCurrentContent();
const contentJSON = convertToRaw(contentState);
console.log(contentJSON); // 可保存到数据库或发送到服务器

// 从JSON恢复内容
import { convertFromRaw } from 'draft-js';
const restoredContent = convertFromRaw(savedContentJSON);
const newEditorState = EditorState.createWithContent(restoredContent);

相关API文档:docs/APIReference-Data-Conversion.md

2. 处理图片上传

Draft.js通过Entity机制支持图片等媒体内容,以下是简化实现:

import { AtomicBlockUtils } from 'draft-js';

// 插入图片
insertImage = (url) => {
  const contentState = this.state.editorState.getCurrentContent();
  const contentStateWithEntity = contentState.createEntity(
    'IMAGE',
    'IMMUTABLE',
    { src: url }
  );
  const entityKey = contentStateWithEntity.getLastCreatedEntityKey();
  const newEditorState = EditorState.set(
    this.state.editorState,
    { currentContent: contentStateWithEntity }
  );
  this.onChange(
    AtomicBlockUtils.insertAtomicBlock(newEditorState, entityKey, ' ')
  );
};

// 渲染图片块
const blockRendererFn = (contentBlock) => {
  if (contentBlock.getType() === 'atomic') {
    return {
      component: ImageBlock,
      editable: false,
    };
  }
  return null;
};

// 图片组件
const ImageBlock = (props) => {
  const { contentState, block } = props;
  const entity = contentState.getEntity(block.getEntityAt(0));
  const { src } = entity.getData();
  return <img src={src} style={{ maxWidth: '100%' }} />;
};

完整图片上传示例:examples/draft-0-10-0/media/media.html

3. 自定义快捷键

通过keyBindingFn自定义快捷键:

const keyBindingFn = (e) => {
  if (e.keyCode === 83 && e.metaKey) { // Cmd+S
    this.saveContent();
    return 'save-content';
  }
  return getDefaultKeyBinding(e);
};

// 在Editor组件中使用
<Editor
  editorState={editorState}
  onChange={onChange}
  keyBindingFn={keyBindingFn}
  handleKeyCommand={(command) => {
    if (command === 'save-content') {
      // 处理保存逻辑
      return 'handled';
    }
    return 'not-handled';
  }}
/>

快捷键文档:docs/Advanced-Topics-Key-Bindings.md

项目资源与学习路径

官方示例库

进阶学习资源

总结与下一步

通过本文,你已掌握Draft.js的核心使用方法,能够构建基础的富文本编辑器。下一步建议:

  1. 克隆官方仓库,运行完整示例:
git clone https://gitcode.com/gh_mirrors/dra/draft-js
cd draft-js/examples/draft-0-10-0/rich
open rich.html
  1. 根据需求扩展功能:
  • 添加表格支持:使用第三方库如draft-js-table-plugin
  • 实现代码高亮:结合draft-js-prism
  • 集成Markdown语法:参考examples/draft-0-10-0/tex
  1. 深入学习不可变数据结构,理解Draft.js状态管理原理

Draft.js虽然入门有一定门槛,但其灵活性和React生态整合能力使其成为构建复杂富文本编辑器的理想选择。官方文档和示例是最好的学习资源,建议结合实际项目需求逐步深入。

【免费下载链接】draft-js A React framework for building text editors. 【免费下载链接】draft-js 项目地址: https://gitcode.com/gh_mirrors/dra/draft-js

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值