5分钟上手Draft.js:从零构建React富文本编辑器,告别复杂配置
你还在为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):切换内联样式,支持BOLD、ITALIC、UNDERLINE等预设样式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
项目资源与学习路径
官方示例库
- 基础文本编辑:examples/draft-0-10-0/plaintext/plaintext.html
- 富文本完整示例:examples/draft-0-10-0/rich/rich.html
- TeX公式编辑:examples/draft-0-10-0/tex/readme.md
- 社交媒体卡片:examples/draft-0-10-0/tweet/tweet.html
进阶学习资源
- 状态管理深入理解:docs/Advanced-Topics-EditorState-Race-Conditions.md
- 自定义装饰器:docs/Advanced-Topics-Decorators.md
- 性能优化:docs/Advanced-Topics-Issues-and-Pitfalls.md
总结与下一步
通过本文,你已掌握Draft.js的核心使用方法,能够构建基础的富文本编辑器。下一步建议:
- 克隆官方仓库,运行完整示例:
git clone https://gitcode.com/gh_mirrors/dra/draft-js
cd draft-js/examples/draft-0-10-0/rich
open rich.html
- 根据需求扩展功能:
- 添加表格支持:使用第三方库如
draft-js-table-plugin - 实现代码高亮:结合
draft-js-prism - 集成Markdown语法:参考examples/draft-0-10-0/tex
- 深入学习不可变数据结构,理解Draft.js状态管理原理
Draft.js虽然入门有一定门槛,但其灵活性和React生态整合能力使其成为构建复杂富文本编辑器的理想选择。官方文档和示例是最好的学习资源,建议结合实际项目需求逐步深入。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



