news 2026/9/13 14:11:13

Lexical 只读模式(Read Mode)与编辑模式(Edit Mode)完整指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Lexical 只读模式(Read Mode)与编辑模式(Edit Mode)完整指南

Lexical 只读模式(Read Mode)与编辑模式(Edit Mode)完整指南

【免费下载链接】lexicalLexical is an extensible text editor framework that provides excellent reliability, accessibility and performance.项目地址: https://gitcode.com/GitHub_Trending/le/lexical

导读

本文围绕 Lexical 编辑器的两种工作模式——只读模式(Read Mode)编辑模式(Edit Mode)——展开。无论你是在构建文档预览、评论展示、聊天记录回放,还是需要临时锁定编辑器内容防止误编辑,掌握editable配置与setEditable()/isEditable()/registerEditableListener()这套 API 都是基础中的基础。读完本文,你将能够:在编辑器创建时或运行期任意切换只读/编辑模式、精确读取当前模式、监听模式切换事件驱动 UI 联动,并理解其底层基于contentEditable的实现原理与事件拦截机制。


1. 什么是 Read Mode / Edit Mode

Lexical 支持两种模式:

  • Read mode(只读模式)editablefalse,编辑器不再接受用户输入,适合预览、回放、审核等场景;
  • Edit mode(编辑模式)editabletrue,编辑器正常接收输入,是 Lexical 的默认行为。

文档原文明确指出:"The default behavior for Lexical is edit mode, or more accurately not read only mode."换句话说,默认值就是可编辑,无需任何显式配置。

从底层实现看,两种模式的本质区别在于根 DOM 元素(contentEditable)上contenteditable属性的设置。在 packages/lexical/src/LexicalUtils.ts 中可以看到,Lexical 会根据编辑器的可编辑状态把元素的contentEditable设为'true''false'(相关实现位于 LexicalUtils.ts 的contentEditable赋值逻辑附近)。这是浏览器原生能力与 Lexical 模式系统的衔接点:只读时浏览器本身就拒绝一切键盘/鼠标文本编辑,从而在根源上杜绝了用户改动内容。

模式切换对插件同样可见:特定插件可以监听模式变化(详见下文registerEditableListener),根据模式自定义部分 UI——例如只读时隐藏工具栏按钮、切换为"阅读视图"样式,或在编辑模式下才显示拖拽手柄。


2. 设置模式:创建时配置与运行期切换

2.1 在创建编辑器时设置

使用核心包lexical时,在createEditor的配置对象中传入editable

import {createEditor} from 'lexical'; const editor = createEditor({ editable: true, // ...其余配置:nodes、theme、onError 等 });

editable是一个可选布尔值。查看 createEditor 的实现可以发现默认值的处理逻辑:

const isEditable = config.editable !== undefined ? config.editable : true;

也就是说,只要不传editable,默认即为true(编辑模式)。该值随后在编辑器构造时被存入内部字段this._editable = editable(见 LexicalEditor.ts),并贯穿整个编辑器生命周期。

2.2 在 React 中使用<LexicalComposer>设置

如果你使用@lexical/react,模式是在传给<LexicalComposer>initialConfig中配置的:

import {LexicalComposer} from '@lexical/react/LexicalComposer'; <LexicalComposer initialConfig={{editable: true}}> {/* 在这里放置 RichTextPlugin、ContentEditable、工具栏等 */} </LexicalComposer>

在 LexicalComposer 源码 中可以看到完整的传递链路:

  1. initialConfig被传入createEditor({editable: initialConfig.editable, ...})(LexicalComposer.tsx);
  2. useLayoutEffect中执行editor.setEditable(isEditable !== undefined ? isEditable : true)(LexicalComposer.tsx),确保挂载后编辑器的实际模式与配置一致。

因此,即使你漏配editable,React 封装也会帮你兜底为true

initialConfig的类型定义(LexicalComposer.tsx)中,editable被明确注释为"initial editable state",即仅在编辑器创建时读取一次。想修改模式,请走下面的命令式 API。

2.3 创建之后命令式切换模式

编辑器创建后,可以在任意时刻通过editor.setEditable()命令式切换:

// 切换到只读 editor.setEditable(false); // 恢复可编辑 editor.setEditable(true);

底层实现在 LexicalEditor.ts:

setEditable(editable: boolean): void { if (this._editable !== editable) { this._editable = editable; triggerListeners('editable', this, true, editable); // ... } }

值得注意的实现细节:

  • 去重保护:只有当新值与当前值不同时才真正触发变更,重复设置相同值不会产生多余事件;
  • 主动通知:变更后立即触发editable监听器(即registerEditableListener注册的回调);
  • DOM 联动:若编辑器使用了 named-slot 等机制,还会触发一次 reconcile 更新,把只读状态同步到相关 DOM 元素上(见 LexicalEditor.ts 的注释与$fullReconcile()调用)。

提示:setEditable是同步方法,调用后立刻调用isEditable()即可读到新值,无需等待下一次更新提交。


3. 读取模式:isEditable 与监听器

3.1 查询当前模式

使用editor.isEditable()获取当前编辑器的可编辑状态:

const isEditable = editor.isEditable(); // true 或 false

实现非常直接(LexicalEditor.ts):

isEditable(): boolean { return this._editable; }

3.2 监听模式变化:registerEditableListener

如果你需要在模式切换时得到通知(例如隐藏/显示工具栏、切换只读样式),可以注册一个可编辑状态监听器:

const removeEditableListener = editor.registerEditableListener( (isEditable) => { // 回调参数即当前模式 console.log(isEditable); }, ); // 不再需要时务必注销,防止内存泄漏 removeEditableListener();

registerEditableListener的实现(LexicalEditor.ts)将回调注册到内部的editable监听器集合,并返回一个销毁函数(teardown function),调用它即可在组件卸载或不再需要时解除监听。这一点在文档中也被特别强调:"Do not forget to unregister the listener when no longer needed!"

结合setEditable的源码可以推断出完整行为闭环:

  1. 调用setEditable(newValue)
  2. 内部比对发现值变化;
  3. triggerListeners('editable', this, true, editable)将所有已注册回调以新布尔值调用;
  4. 返回值removeEditableListener()用于事后清理。

3.3 单元测试佐证

仓库的单元测试 packages/lexical/src/tests/unit/LexicalEditor.test.tsx 对该行为有明确的验证(editable listener用例,位于文件 LexicalEditor.test.tsx 中):

const editableFn = vi.fn(); editor.registerEditableListener(editableFn); expect(editor.isEditable()).toBe(true); // 默认可编辑 editor.setEditable(false); expect(editor.isEditable()).toBe(false); // 切换为只读 editor.setEditable(true); expect(editableFn.mock.calls).toEqual([[false], [true]]); // 回调依次收到 false、true

该测试证实了三件事:默认模式为可编辑、setEditableisEditable立即反映新值、监听器在每次有效切换时被触发且参数为最新布尔值。此外,测试中还有setEditable与根元素相关的用例(例如 "Retains pendingEditor while rootNode is not set" 遍历[true, false]两种模式),覆盖了只读/可编辑两种模式下的状态一致性场景。


4. 在 React 中订阅模式:useLexicalEditable

如果你使用@lexical/react且需要响应式地读取模式,官方推荐使用useLexicalEditable()Hook,而不是手动注册监听器。其实现位于 packages/lexical-react/src/useLexicalEditable.ts:

function subscription(editor: LexicalEditor): LexicalSubscription<boolean> { return { initialValueFn: () => editor.isEditable(), subscribe: callback => { return editor.registerEditableListener(callback); }, }; } export function useLexicalEditable(): boolean { return useLexicalSubscription(subscription); }

该 Hook 在内部使用useLexicalSubscription完成订阅,在组件中直接返回当前的isEditable布尔值,并在模式变化时触发重渲染。其 JSDoc 注释明确说明:手动用registerEditableListener观察该值"比较棘手,尤其是在 React StrictMode(开发环境默认开启)或并发模式下",因此建议优先使用本 Hook。

仓库内部的实践佐证——以下插件均通过useLexicalEditable()响应式切换 UI:

  • LexicalRichTextPlugin.tsx 与 LexicalPlainTextPlugin.tsx:根据editable决定是否渲染输入相关元素;
  • LexicalDraggableBlockPlugin.tsx:根据useLexicalEditable()决定拖拽手柄的渲染与更新(注释还特别提到该 Hook 能正确处理 StrictMode 场景)。

典型用法:

import {useLexicalEditable} from '@lexical/react/useLexicalEditable'; function Toolbar() { const isEditable = useLexicalEditable(); return ( <div> {isEditable ? ( <button onClick={/* 加粗 */}>B</button> ) : ( <span>只读模式,工具栏已隐藏</span> )} </div> ); }

5. 底层原理:contentEditable 与事件拦截

文档指出,模式的底层实现细节是contentEditable被设为"false""true"。结合源码可以进一步确认两点:

第一,事件层面的拦截。在 packages/lexical/src/LexicalEvents.ts 中,大量原生事件(mousedown、keydown、beforeinput、composition 等)的处理器都会先检查editor.isEditable(),例如:

if (editor.isEditable() || eventName === 'click') { // 只有可编辑时才处理输入类事件;click 例外,仍允许处理 }

这意味着:即便某些浏览器行为绕过contenteditable=false,Lexical 的事件系统也会在内部再次把关,双保险地保证只读模式下的内容安全。其中click事件在只读时依然会被处理,从而保证选中、光标定位、链接点击等非破坏性交互可用。

第二,DOM 属性同步。LexicalUtils.ts 中处理 named-slot 等可编辑"孤岛"时,会根据editor.isEditable()显式设置element.contentEditable = editable ? 'true' : 'false',确保内嵌的编辑器子树与主编辑器的只读状态保持一致。


6. 实战场景与最佳实践

6.1 常见应用场景

  • 文档预览 / 详情页:展示已保存的富文本内容,editable: false,配合自定义只读样式;
  • 评论与聊天记录:历史消息只读、输入框可编辑,二者可共存于同一页面;
  • 权限控制:根据用户角色在编辑器创建时决定是否允许编辑;
  • 表单锁定:编辑中提交后setEditable(false)锁定,防止再次修改;
  • 回放 / 演示:定时切换模式,模拟"用户正在输入"的演示效果。

6.2 实践建议

  1. 默认值不用显式写:需要可编辑时不写editable即可(默认为true),代码更简洁;
  2. 创建后切换走命令式 APIinitialConfig.editable只在创建时生效,动态切换请用editor.setEditable()
  3. React 组件内优先用useLexicalEditable():可避免 StrictMode 下的订阅问题,并自动触发重渲染;
  4. 务必注销监听器:手动registerEditableListener时保留返回的 teardown 函数,在组件卸载或作用域结束时调用,防止内存泄漏;
  5. 只读模式依然可交互click等非破坏性事件在只读模式下仍被处理,可放心依赖选中、聚焦等行为;
  6. 结合主题与插件联动:模式变化事件可驱动工具栏显隐、只读样式切换,参考LexicalDraggableBlockPlugin的做法。

7. 小结

Lexical 的 Read Mode / Edit Mode 是一套简洁而完整的模式系统:

能力API说明
创建时配置createEditor({editable})/<LexicalComposer initialConfig={{editable}}>默认true,仅在创建时读取
运行期切换editor.setEditable(boolean)同步生效,值未变化时不触发事件
查询当前模式editor.isEditable()返回当前布尔值
监听模式变化editor.registerEditableListener(cb)返回 teardown 函数用于注销
React 响应式订阅useLexicalEditable()基于useLexicalSubscription,推荐在组件内使用

从源码层面看,模式的本质是contentEditable属性的 true/false 切换,辅以 LexicalEvents.ts 中的事件级双重拦截,确保只读模式真正"读不可写";而监听器机制(registerEditableListener)让插件与 UI 能够随模式变化实时联动,这也正是 Lexical 可扩展性的体现之一。

相关源码索引

  • 编辑器核心与setEditable/isEditable/registerEditableListener实现:packages/lexical/src/LexicalEditor.ts
  • createEditoreditable默认值处理:packages/lexical/src/LexicalEditor.ts
  • 事件层isEditable拦截:packages/lexical/src/LexicalEvents.ts
  • React 封装与initialConfig.editable传递:packages/lexical-react/src/LexicalComposer.tsx
  • 响应式订阅 Hook:packages/lexical-react/src/useLexicalEditable.ts
  • 单元测试验证:packages/lexical/src/tests/unit/LexicalEditor.test.tsx

【免费下载链接】lexicalLexical is an extensible text editor framework that provides excellent reliability, accessibility and performance.项目地址: https://gitcode.com/GitHub_Trending/le/lexical

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 14:09:41

Codex macOS客户端实测:从写代码到指挥智能体团队

Codex 的 macOS 客户端正式发布那天&#xff0c;我第一时间装上了&#xff0c;连着用了两周&#xff0c;把之前命令行版本没敢试的场景全试了一遍。先说结论&#xff1a;它和 GitHub Copilot、Cursor 这类工具完全不是一个物种。Copilot 是你写代码时它帮你补全&#xff0c;Cur…

作者头像 李华
网站建设 2026/9/13 14:08:44

基于STM32F103ZET6的步进小车四合一控制源码解析

简介&#xff1a;STM32F103ZET6步进电机智能小车完整程序源码基于Keil开发环境编写&#xff0c;支持红外遥控、避障、跟随、循迹四种工作模式&#xff0c;适合电子竞赛、课程设计与智能小车自制等场景。系统以ULN2003驱动28BYJ-48步进电机&#xff0c;通过VS1838B接收红外遥控信…

作者头像 李华
网站建设 2026/9/13 14:07:47

AI如何优化博士论文写作:逻辑校验与结构优化

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 14:07:31

STM32F103ZET6上STemWin TTF矢量字体显示实战:从原理到排错

简介&#xff1a;面向 STM32F103ZET6 开发者的 STemWin 图形界面实验例程包&#xff0c;聚焦 TTF 格式字体显示功能&#xff0c;适合希望在资源有限的嵌入式平台实现高质量文字渲染的工程师或学习者。压缩包共 954 个文件&#xff0c;约 25.65MB&#xff0c;其中包含 409 个头文…

作者头像 李华