多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

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

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 ModeLexical 支持两种模式Read mode只读模式editable为false编辑器不再接受用户输入适合预览、回放、审核等场景Edit mode编辑模式editable为true编辑器正常接收输入是 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的配置对象中传入editableimport {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 源码 中可以看到完整的传递链路initialConfig被传入createEditor({editable: initialConfig.editable, ...})LexicalComposer.tsx在useLayoutEffect中执行editor.setEditable(isEditable ! undefined ? isEditable : true)LexicalComposer.tsx确保挂载后编辑器的实际模式与配置一致。因此即使你漏配editableReact 封装也会帮你兜底为true。initialConfig的类型定义LexicalComposer.tsx中editable被明确注释为initial editable state即仅在编辑器创建时读取一次。想修改模式请走下面的命令式 API。2.3 创建之后命令式切换模式编辑器创建后可以在任意时刻通过editor.setEditable()命令式切换// 切换到只读 editor.setEditable(false); // 恢复可编辑 editor.setEditable(true);底层实现在 LexicalEditor.tssetEditable(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.tsisEditable(): 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的源码可以推断出完整行为闭环调用setEditable(newValue)内部比对发现值变化triggerListeners(editable, this, true, editable)将所有已注册回调以新布尔值调用返回值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该测试证实了三件事默认模式为可编辑、setEditable后isEditable立即反映新值、监听器在每次有效切换时被触发且参数为最新布尔值。此外测试中还有setEditable与根元素相关的用例例如 Retains pendingEditor while rootNode is not set 遍历[true, false]两种模式覆盖了只读/可编辑两种模式下的状态一致性场景。4. 在 React 中订阅模式useLexicalEditable如果你使用lexical/react且需要响应式地读取模式官方推荐使用useLexicalEditable()Hook而不是手动注册监听器。其实现位于 packages/lexical-react/src/useLexicalEditable.tsfunction subscription(editor: LexicalEditor): LexicalSubscriptionboolean { 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()响应式切换 UILexicalRichTextPlugin.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 例外仍允许处理 }这意味着即便某些浏览器行为绕过contenteditablefalseLexical 的事件系统也会在内部再次把关双保险地保证只读模式下的内容安全。其中click事件在只读时依然会被处理从而保证选中、光标定位、链接点击等非破坏性交互可用。第二DOM 属性同步。LexicalUtils.ts 中处理 named-slot 等可编辑孤岛时会根据editor.isEditable()显式设置element.contentEditable editable ? true : false确保内嵌的编辑器子树与主编辑器的只读状态保持一致。6. 实战场景与最佳实践6.1 常见应用场景文档预览 / 详情页展示已保存的富文本内容editable: false配合自定义只读样式评论与聊天记录历史消息只读、输入框可编辑二者可共存于同一页面权限控制根据用户角色在编辑器创建时决定是否允许编辑表单锁定编辑中提交后setEditable(false)锁定防止再次修改回放 / 演示定时切换模式模拟用户正在输入的演示效果。6.2 实践建议默认值不用显式写需要可编辑时不写editable即可默认为true代码更简洁创建后切换走命令式 APIinitialConfig.editable只在创建时生效动态切换请用editor.setEditable()React 组件内优先用useLexicalEditable()可避免 StrictMode 下的订阅问题并自动触发重渲染务必注销监听器手动registerEditableListener时保留返回的 teardown 函数在组件卸载或作用域结束时调用防止内存泄漏只读模式依然可交互click等非破坏性事件在只读模式下仍被处理可放心依赖选中、聚焦等行为结合主题与插件联动模式变化事件可驱动工具栏显隐、只读样式切换参考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.tscreateEditor及editable默认值处理packages/lexical/src/LexicalEditor.ts事件层isEditable拦截packages/lexical/src/LexicalEvents.tsReact 封装与initialConfig.editable传递packages/lexical-react/src/LexicalComposer.tsx响应式订阅 Hookpackages/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),仅供参考
返回列表