1. 项目概述:React富文本编辑器的核心挑战
在Web开发领域,富文本编辑器一直是个"看起来简单做起来难"的典型组件。我最近用React从头实现了一个富文本编辑器,深刻体会到其中的技术门道。与直接使用现成库不同,原生实现让我们必须直面几个关键问题:
-
内容可编辑性的本质:浏览器提供的contentEditable属性看似简单,实则暗藏玄机。不同浏览器对回车、退格等操作的处理差异巨大,需要大量兼容性处理。
-
状态管理的复杂性:编辑器需要同时维护DOM状态和React状态,两者同步是个技术活。直接操作DOM会破坏React的虚拟DOM机制,完全依赖React状态又会导致性能问题。
-
选区与光标控制:当用户点击或使用键盘导航时,如何精准控制光标位置?特别是在插入自定义组件时,光标行为变得异常复杂。
-
自定义节点的扩展:现代编辑器需要支持嵌入图片、视频、表格等复杂内容,这些"非文本"节点如何与文本内容和谐共处?
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心架构设计
2.1 技术选型决策
经过多次迭代,我最终确定了以下技术方案:
jsx复制// 编辑器核心容器
function RichTextEditor() {
const [value, setValue] = useState(initialValue);
const editorRef = useRef(null);
// 关键设计:使用React Portal处理自定义节点
return (
<div
ref={editorRef}
contentEditable
onInput={handleInput}
dangerouslySetInnerHTML={{__html: value}}
>
{renderCustomNodes()}
</div>
)
}
选择这种混合方案(contentEditable + React控制)主要基于:
- 性能考量:纯React方案在大量文本时会有明显卡顿
- 功能完整性:需要直接利用浏览器原生的编辑能力
- 扩展性需求:必须支持插入React组件作为自定义节点
2.2 数据模型设计
编辑器内部维护两套并行数据:
- HTML字符串:用于直接渲染到contentEditable区域
- JSON树结构:记录所有节点的结构化信息,特别是自定义组件的位置和属性
javascript复制// 典型的数据结构
{
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{"type": "text", "text": "Hello "},
{"type": "m
