如何为 Lexical Extension 做按需懒加载(register 中动态 import)?
【免费下载链接】lexicalLexical is an extensible text editor framework that provides excellent reliability, accessibility and performance.项目地址: https://gitcode.com/GitHub_Trending/le/lexical
Lexical 编辑器由一组 Extension 通过buildEditorFromExtensions构建,其中一些 Extension(表格、代码高亮、AI 功能等)实现体积不小,用户未必使用。目标是把这些 Extension 的运行时实现推迟到真正需要时才加载,而不是随编辑器主 bundle 一起加载。
前提条件:编辑器必须通过 Lexical Extension API 创建,即buildEditorFromExtensions,或使用它内部抽象的LexicalExtensionComposer。Legacy React Plugin 与 Lexical Extensions 兼容,但懒加载机制依赖 Extension 的register/afterRegistration生命周期。
register 阶段为什么可以异步 import
官方 FAQ(packages/lexical-website/docs/extensions/faq.mdx)明确了懒加载的三条规则:
- 不需要框架层面的特殊支持。Extension 中你实现的所有函数必须是同步的,但
register或afterRegistration函数内部可以执行异步操作:动态import()、网络请求等。 - 必须管好清理。你从
register返回的 dispose 函数要能正确管理清理;也可以用 state 上的getSignal()拿到一个AbortSignal,文档说这比在 dispose 里手写清理更方便。 - Extension 本身必须同步导入。在创建编辑器之前,Extension 必须已经同步 import 完毕——Lexical 需要全部编辑器配置(包括 nodes)在创建前就绪。懒加载推迟的是
register里的实现模块,不是 Extension 定义本身。
register与afterRegistration的时机说明见 packages/lexical-website/docs/extensions/defining-extensions.md:register发生在编辑器构建完成之后,适合注册命令、监听器等;afterRegistration发生在所有 Extension 的register都调用完之后。两者返回的都是 dispose 函数。
最小可运行的懒加载 Extension
以下写法直接取自示例 examples/extension-vanilla-tailwind/src/main.ts 中的LazyExtension(示例中的模块路径是./lazyLoaded):
import {defineExtension} from 'lexical'; const LazyExtension = defineExtension({ name: '@lexical/extension-vanilla-tailwind-example/Lazy', register(editor, _config, state) { let dispose: undefined | (() => void); import('./lazyLoaded').then(mod => { if (!state.getSignal().aborted) { dispose = mod.registerLazyLoaded(editor); } }); return () => { if (dispose) { dispose(); } }; }, });逐段说明:
import('./lazyLoaded')是动态 import,构建工具会把它拆成独立 chunk,只有该 Promise 执行时才下载并运行目标模块;state.getSignal().aborted用于处理竞争条件:编辑器在模块加载完成前被销毁时,signal 已 aborted,此时不应再调用registerLazyLoaded,否则会给已销毁的编辑器注册监听器;registerLazyLoaded(editor)返回的清理函数存到dispose,register自身返回的 dispose 函数在编辑器销毁时调用它。
LazyExtension作为普通 Extension 放入buildEditorFromExtensions的dependencies数组即可,与同步 Extension 没有区别。
被懒加载的模块怎么写
参考 examples/extension-vanilla-tailwind/src/lazyLoaded.ts。它的完整逻辑是:导出一个registerLazyLoaded(editor)函数,函数内部创建一个触发按钮(挂载到index.html中的#lazy-loaded容器),并注册LAZY_LOADED_LOWERCASE_COMMAND命令,命令处理逻辑是把编辑器内所有文本节点转为小写:
export function registerLazyLoaded(editor: LexicalEditor): () => void { // 创建并挂载触发按钮(示例中挂载到 index.html 的 #lazy-loaded 容器) // ... return editor.registerCommand( LAZY_LOADED_LOWERCASE_COMMAND, () => { for (const node of $getRoot().getAllTextNodes()) { const text = node.getTextContent(); const lower = text.toLowerCase(); if (text !== lower) { node.setTextContent(lower); } } return true; }, COMMAND_PRIORITY_EDITOR, ); }COMMAND_PRIORITY_EDITOR与createCommand来自lexical包。你的实际场景里,这个函数内部换成自己的命令注册、监听器或 DOM 操作即可,只要返回值是可用于清理的 dispose 函数。这个文件只有被动态 import 时才执行,不会进入编辑器初始 bundle。
完整可运行示例是 examples/extension-vanilla-tailwind,按 README 说明,pnpm i && pnpm run dev启动。
如何验证懒加载生效
- 启动后打开浏览器开发者工具的 Network 面板:初始 HTML 加载列表中不应出现被懒加载模块的 JS 文件;该 chunk 只应在编辑器实例化触发
register后出现。 - 触发功能:在示例中点击按钮,编辑器内文本全部变为小写,说明命令注册成功、模块加载链路完整。
- 清理路径:卸载/销毁编辑器时
register返回的 dispose 会执行;被懒加载模块注册的命令、监听器应当随之移除,不会残留引用。
限制
懒加载只覆盖register/afterRegistration阶段的运行时行为。Extension 的定义与配置——包括nodes、theme、html等——仍需在创建编辑器前同步导入,因为 Lexical 要求所有编辑器配置(包括 nodes)在创建前就绪。官方 FAQ 的结论是:这与不使用 Extension 时的情况没有区别,Lexical 核心不允许运行时变更编辑器配置。
这个机制与React.lazy的思路类似(文档原文的类比):Extension 定义先行,实现推迟到使用时刻。
【免费下载链接】lexicalLexical is an extensible text editor framework that provides excellent reliability, accessibility and performance.项目地址: https://gitcode.com/GitHub_Trending/le/lexical
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考