lexical

lexical

高度可扩展的JavaScript网页文本编辑框架

Lexical是一个专注于可靠性、可访问性和性能的JavaScript网页文本编辑框架。它为开发者提供优秀的开发体验,便于创建独特的文本编辑功能。Lexical采用高度可扩展的架构,支持简单到复杂的多种编辑需求,可根据项目进行定制。这个框架适用于不同规模的文本编辑项目开发。

Lexical文本编辑器JavaScriptReact开源框架Github开源项目
<h1 align="center"> <a href="https://lexical.dev">Lexical</a> </h1> <p align="center"> <img alt="GitHub工作流状态" src="https://img.shields.io/github/actions/workflow/status/facebook/lexical/tests.yml"/> <a href="https://www.npmjs.com/package/lexical"> <img alt="访问NPM页面" src="https://img.shields.io/npm/v/lexical"/> </a> <a href="https://discord.gg/KmG4wQnnD9"> <img alt="加入我们的Discord" src="https://img.shields.io/discord/953974421008293909"/> </a> <a href="https://twitter.com/intent/follow?screen_name=lexicaljs"> <img alt="在Twitter上关注我们" src="https://img.shields.io/twitter/follow/lexicaljs?style=social"/> </a> </p>

Lexical是一个可扩展的JavaScript网页文本编辑器框架,注重可靠性、可访问性和性能。Lexical旨在提供一流的开发者体验,让您能够轻松地进行原型设计和功能构建。结合高度可扩展的架构,Lexical允许开发者创建独特的、可扩展的文本编辑体验。

有关Lexical的文档和更多信息,请务必访问Lexical网站

以下是一些使用Lexical可以实现的例子:


概述:


React入门

注意:Lexical不仅限于React。只要为特定库创建了绑定,Lexical就可以支持任何基于DOM的底层库。

安装lexical@lexical/react

npm install --save lexical @lexical/react

以下是使用lexical@lexical/react的基本纯文本编辑器示例(自己试试)。

import {$getRoot, $getSelection} from 'lexical'; import {useEffect} from 'react'; import {LexicalComposer} from '@lexical/react/LexicalComposer'; import {PlainTextPlugin} from '@lexical/react/LexicalPlainTextPlugin'; import {ContentEditable} from '@lexical/react/LexicalContentEditable'; import {HistoryPlugin} from '@lexical/react/LexicalHistoryPlugin'; import {OnChangePlugin} from '@lexical/react/LexicalOnChangePlugin'; import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext'; import {LexicalErrorBoundary} from '@lexical/react/LexicalErrorBoundary'; const theme = { // 主题样式在这里 // ... } // 当编辑器发生变化时,您可以通过LexicalOnChangePlugin获得通知! function onChange(editorState) { editorState.read(() => { // 在这里读取EditorState的内容。 const root = $getRoot(); const selection = $getSelection(); console.log(root, selection); }); } // Lexical React插件是React组件,这使它们具有高度的可组合性。 // 此外,您可以按需懒加载插件,这样在实际使用之前不会产生插件的开销。 function MyCustomAutoFocusPlugin() { const [editor] = useLexicalComposerContext(); useEffect(() => { // 当effect触发时,聚焦编辑器! editor.focus(); }, [editor]); return null; } // 捕获Lexical更新期间发生的任何错误并记录它们 // 或根据需要抛出它们。如果不抛出,Lexical将 // 尝试优雅地恢复而不丢失用户数据。 function onError(error) { console.error(error); } function Editor() { const initialConfig = { namespace: 'MyEditor', theme, onError, }; return ( <LexicalComposer initialConfig={initialConfig}> <PlainTextPlugin contentEditable={<ContentEditable />} placeholder={<div>输入一些文本...</div>} ErrorBoundary={LexicalErrorBoundary} /> <OnChangePlugin onChange={onChange} /> <HistoryPlugin /> <MyCustomAutoFocusPlugin /> </LexicalComposer> ); }

Lexical是一个框架

Lexical的核心是一个无依赖的文本编辑器框架,允许开发者构建强大的、简单和复杂的编辑器界面。Lexical有一些值得探索的概念:

编辑器实例

编辑器实例是将所有内容连接在一起的核心。您可以将可编辑的DOM元素附加到编辑器实例,并注册监听器和命令。最重要的是,编辑器允许更新其EditorState。您可以使用createEditor() API创建一个编辑器实例,但在使用如@lexical/react这样的框架绑定时通常不需要担心这一点,因为这已经为您处理好了。

编辑器状态

编辑器状态是表示您想在DOM上显示的内容的底层数据模型。编辑器状态包含两个部分:

  • Lexical节点树
  • Lexical选择对象

一旦创建,编辑器状态是不可变的,要创建一个编辑器状态,您必须通过editor.update(() => {...})来完成。但是,您也可以使用节点转换或命令处理程序"挂钩"到现有的更新中 - 这些在现有的更新工作流程中调用,以防止更新的级联/瀑布效应。您可以使用editor.getEditorState()检索当前的编辑器状态。

编辑器状态也可以完全序列化为JSON,并且可以使用editor.parseEditorState()轻松地反序列化回编辑器。

读取和更新编辑器状态

当你想要读取和/或更新词法节点树时,必须通过 editor.update(() => {...}) 来执行。你也可以通过 editor.read(() => {...})editor.getEditorState().read(() => {...}) 对编辑器状态进行只读操作。

传递给 update 或 read 调用的闭包很重要,且必须是同步的。这是你拥有完整"词法"上下文的活动编辑器状态的唯一地方,并为你提供访问编辑器状态节点树的权限。我们提倡使用带有 $ 前缀的函数(如 $getRoot())的约定,以表明这些函数必须在此上下文中调用。在 read 或 update 之外尝试使用它们将触发运行时错误。

对于熟悉 React Hooks 的人来说,你可以将这些 $函数视为具有类似功能:

特性React HooksLexical $函数
命名约定useFunction$function
所需上下文只能在渲染时调用只能在 update 或 read 中调用
可组合Hooks 可以调用其他 hooks$函数可以调用其他 $函数
必须是同步的
其他规则❌ 必须无条件地以相同顺序调用✅ 无

节点转换和命令监听器是在隐式的 editor.update(() => {...}) 上下文中调用的。

允许进行嵌套更新或嵌套读取,但更新不应嵌套在读取中,反之亦然。例如,editor.update(() => editor.update(() => {...})) 是允许的。允许在 editor.update 的末尾嵌套 editor.read,但这会立即刷新更新,并且在该回调中的任何额外更新都会抛出错误。

所有 Lexical 节点都依赖于相关的编辑器状态。除了少数例外,你应该只在 read 或 update 调用中调用 Lexical 节点的方法和访问属性(就像 $ 函数一样)。Lexical 节点上的方法首先会尝试使用节点的唯一键从活动编辑器状态中定位最新(可能是可写的)版本的节点。一个逻辑节点的所有版本都有相同的键。这些键由编辑器管理,仅在运行时存在(不序列化),应被视为随机和不透明的(不要编写假设键有硬编码值的测试)。

这样做是因为编辑器状态的节点树在协调后会被递归冻结,以支持高效的时间旅行(撤销/重做和类似用例)。更新节点的方法首先调用 node.getWritable(),这将创建一个冻结节点的可写克隆。这通常意味着任何现有引用(如局部变量)都会引用节点的过时版本,但让 Lexical 节点始终引用编辑器状态可以实现更简单、更不容易出错的数据模型。

:::提示

如果你使用 editor.read(() => { /* 回调 */ }),它会首先刷新任何待处理的更新,因此你总会看到一个一致的状态。当你在 editor.update 中时,你总是在处理待处理的状态,其中节点转换和 DOM 协调可能还没有运行。editor.getEditorState().read() 将使用最新协调的 EditorState(在任何节点转换、DOM 协调等已经运行之后),任何待处理的 editor.update 变更尚未可见。

:::

DOM 协调器

Lexical 有自己的 DOM 协调器,它接收一组编辑器状态(总是"当前"和"待处理"的),并对它们应用"差异"。然后它使用这个差异来只更新 DOM 中需要改变的部分。你可以将此视为一种虚拟 DOM,但 Lexical 能够跳过大部分差异比较工作,因为它知道在给定的更新中发生了什么变化。DOM 协调器采用了有利于内容可编辑典型启发式的性能优化,并能够自动确保 LTR 和 RTL 语言的一致性。

监听器、节点转换和命令

除了调用更新外,使用 Lexical 的大部分工作是通过监听器、节点转换和命令完成的。这些都源于编辑器,并以 register 为前缀。另一个重要特性是所有注册方法都返回一个函数,可以轻松取消订阅。例如,这里展示了如何监听 Lexical 编辑器的更新:

const unregisterListener = editor.registerUpdateListener(({editorState}) => { // 发生了更新! console.log(editorState); }); // 确保稍后移除监听器! unregisterListener();

命令是用于在 Lexical 中连接所有内容的通信系统。可以使用 createCommand() 创建自定义命令,并使用 editor.dispatchCommand(command, payload) 将其分派给编辑器。当触发按键和其他重要信号时,Lexical 内部会分派命令。命令也可以使用 editor.registerCommand(handler, priority) 进行处理,传入的命令会按优先级通过所有处理程序传播,直到某个处理程序停止传播(类似于浏览器中的事件传播)。

使用 Lexical

本节介绍如何独立于任何框架或库使用 Lexical。对于打算在 React 应用程序中使用 Lexical 的人来说,建议查看 @lexical/react 中提供的 hooks 的源代码

创建编辑器并使用它

使用 Lexical 时,通常使用单个编辑器实例。编辑器实例可以被视为负责将 EditorState 与 DOM 连接起来的对象。编辑器也是你可以注册自定义节点、添加监听器和转换的地方。

可以从 lexical 包创建编辑器实例,它接受一个可选的配置对象,允许主题设置和其他选项:

import {createEditor} from 'lexical'; const config = { namespace: 'MyEditor', theme: { ... }, }; const editor = createEditor(config);

一旦你有了编辑器实例,准备就绪后,你可以将编辑器实例与文档中的内容可编辑 <div> 元素关联:

const contentEditableElement = document.getElementById('editor'); editor.setRootElement(contentEditableElement);

如果你想清除编辑器实例与元素的关联,可以传入 null。或者,如果需要,你可以切换到另一个元素,只需将另一个元素引用传递给 setRootElement()

使用编辑器状态

在 Lexical 中,真实来源不是 DOM,而是 Lexical 维护并与编辑器实例相关联的底层状态模型。你可以通过调用 editor.getEditorState() 从编辑器获取最新的编辑器状态。 编辑器状态可序列化为 JSON,编辑器实例提供了一个有用的方法来反序列化字符串化的编辑器状态。

const stringifiedEditorState = JSON.stringify(editor.getEditorState().toJSON()); const newEditorState = editor.parseEditorState(stringifiedEditorState);

更新编辑器

有几种方法可以更新编辑器实例:

  • 使用 editor.update() 触发更新
  • 通过 editor.setEditorState() 设置编辑器状态
  • 通过 editor.registerNodeTransform() 在现有更新中应用更改
  • 使用 editor.registerCommand(EXAMPLE_COMMAND, () => {...}, priority) 的命令监听器

更新编辑器最常见的方式是使用 editor.update()。调用此函数需要传入一个函数,该函数将提供访问权限以修改底层编辑器状态。当开始一个新的更新时,当前编辑器状态会被克隆并用作起点。从技术角度来看,这意味着 Lexical 在更新期间利用了一种称为双缓冲的技术。有一个编辑器状态代表屏幕上当前显示的内容,另一个正在进行中的编辑器状态代表未来的更改。

协调更新通常是一个异步过程,允许 Lexical 将编辑器状态的多个同步更新批量处理为对 DOM 的单次更新 - 从而提高性能。当 Lexical 准备将更新提交到 DOM 时,更新批次中的底层变更和更改将形成一个新的不可变编辑器状态。然后调用 editor.getEditorState() 将返回基于更新中的更改的最新编辑器状态。

以下是如何更新编辑器实例的示例:

import {$getRoot, $getSelection, $createParagraphNode} from 'lexical'; // 在 `editor.update` 内部,你可以使用特殊的 $ 前缀辅助函数。 // 这些函数不能在闭包之外使用,如果尝试使用会报错。 // (如果你熟悉 React,你可以想象这些有点像在 React 函数组件之外使用 hook)。 editor.update(() => { // 从 EditorState 获取 RootNode const root = $getRoot(); // 从 EditorState 获取选择 const selection = $getSelection(); // 创建一个新的 ParagraphNode const paragraphNode = $createParagraphNode(); // 创建一个新的 TextNode const textNode = $createTextNode('Hello world'); // 将文本节点附加到段落 paragraphNode.append(textNode); // 最后,将段落附加到根节点 root.append(paragraphNode); });

如果你想知道编辑器何时更新以便对更改做出反应,你可以向编辑器添加一个更新监听器,如下所示:

editor.registerUpdateListener(({editorState}) => { // 最新的 EditorState 可以在 `editorState` 中找到。 // 要读取 EditorState 的内容,请使用以下 API: editorState.read(() => { // 就像 editor.update(),.read() 期望一个闭包, // 你可以在其中使用 $ 前缀的辅助函数。 }); });

为 Lexical 做贡献

请阅读 CONTRIBUTING.md

可选但推荐,使用 VSCode 进行开发

  1. 下载并安装 VSCode

    • 这里下载(建议使用未修改版本)
  2. 安装扩展

    • Flow Language Support
      • 请确保按照 README 中的步骤进行设置
    • Prettier
      • editor.defaultFormatter 中将 prettier 设置为默认格式化程序
      • 可选:设置保存时格式化 editor.formatOnSave
    • ESlint

文档

浏览器支持

  • Firefox 52+
  • Chrome 49+
  • Edge 79+(当 Edge 切换到 Chromium 时)
  • Safari 11+
  • iOS 11+(Safari)
  • iPad OS 13+(Safari)
  • Android Chrome 72+

注意:Lexical 不支持 Internet Explorer 或旧版 Edge。

贡献

  1. 创建一个新分支
    • git checkout -b my-new-branch
  2. 提交你的更改
    • git commit -a -m '更改描述'
      • 有很多方法可以做到这一点,这只是一个建议
  3. 将你的分支推送到 GitHub
    • git push origin my-new-branch
  4. 转到 GitHub 中的仓库页面,点击"Compare & pull request"
    • GitHub CLI 允许你跳过这一步的网页界面(以及更多操作)

支持

如果你有任何关于 Lexical 的问题,想讨论 bug 报告,或对新集成有疑问,欢迎加入我们的 Discord 服务器

Lexical 工程师会定期查看这里。

运行测试

  • npm run test-unit 仅运行单元测试。
  • npm run test-e2e-chromium 仅运行 chromium e2e 测试。
  • npm run debug-test-e2e-chromium 以调试模式运行 chromium e2e 测试。
  • npm run test-e2e-firefox 仅运行 firefox e2e 测试。
  • npm run debug-test-e2e-firefox 以调试模式运行 firefox e2e 测试。
  • npm run test-e2e-webkit 仅运行 webkit e2e 测试。
  • npm run debug-test-e2e-webkit 以调试模式运行 webkit e2e 测试。

许可证

Lexical 采用 MIT 许可证

编辑推荐精选

Trae

Trae

字节跳动发布的AI编程神器IDE

Trae是一种自适应的集成开发环境(IDE),通过自动化和多元协作改变开发流程。利用Trae,团队能够更快速、精确地编写和部署代码,从而提高编程效率和项目交付速度。Trae具备上下文感知和代码自动完成功能,是提升开发效率的理想工具。

AI工具TraeAI IDE协作生产力转型热门
问小白

问小白

全能AI智能助手,随时解答生活与工作的多样问题

问小白,由元石科技研发的AI智能助手,快速准确地解答各种生活和工作问题,包括但不限于搜索、规划和社交互动,帮助用户在日常生活中提高效率,轻松管理个人事务。

热门AI助手AI对话AI工具聊天机器人
Transly

Transly

实时语音翻译/同声传译工具

Transly是一个多场景的AI大语言模型驱动的同声传译、专业翻译助手,它拥有超精准的音频识别翻译能力,几乎零延迟的使用体验和支持多国语言可以让你带它走遍全球,无论你是留学生、商务人士、韩剧美剧爱好者,还是出国游玩、多国会议、跨国追星等等,都可以满足你所有需要同传的场景需求,线上线下通用,扫除语言障碍,让全世界的语言交流不再有国界。

讯飞智文

讯飞智文

一键生成PPT和Word,让学习生活更轻松

讯飞智文是一个利用 AI 技术的项目,能够帮助用户生成 PPT 以及各类文档。无论是商业领域的市场分析报告、年度目标制定,还是学生群体的职业生涯规划、实习避坑指南,亦或是活动策划、旅游攻略等内容,它都能提供支持,帮助用户精准表达,轻松呈现各种信息。

AI办公办公工具AI工具讯飞智文AI在线生成PPTAI撰写助手多语种文档生成AI自动配图热门
讯飞星火

讯飞星火

深度推理能力全新升级,全面对标OpenAI o1

科大讯飞的星火大模型,支持语言理解、知识问答和文本创作等多功能,适用于多种文件和业务场景,提升办公和日常生活的效率。讯飞星火是一个提供丰富智能服务的平台,涵盖科技资讯、图像创作、写作辅助、编程解答、科研文献解读等功能,能为不同需求的用户提供便捷高效的帮助,助力用户轻松获取信息、解决问题,满足多样化使用场景。

热门AI开发模型训练AI工具讯飞星火大模型智能问答内容创作多语种支持智慧生活
Spark-TTS

Spark-TTS

一种基于大语言模型的高效单流解耦语音令牌文本到语音合成模型

Spark-TTS 是一个基于 PyTorch 的开源文本到语音合成项目,由多个知名机构联合参与。该项目提供了高效的 LLM(大语言模型)驱动的语音合成方案,支持语音克隆和语音创建功能,可通过命令行界面(CLI)和 Web UI 两种方式使用。用户可以根据需求调整语音的性别、音高、速度等参数,生成高质量的语音。该项目适用于多种场景,如有声读物制作、智能语音助手开发等。

咔片PPT

咔片PPT

AI助力,做PPT更简单!

咔片是一款轻量化在线演示设计工具,借助 AI 技术,实现从内容生成到智能设计的一站式 PPT 制作服务。支持多种文档格式导入生成 PPT,提供海量模板、智能美化、素材替换等功能,适用于销售、教师、学生等各类人群,能高效制作出高品质 PPT,满足不同场景演示需求。

讯飞绘文

讯飞绘文

选题、配图、成文,一站式创作,让内容运营更高效

讯飞绘文,一个AI集成平台,支持写作、选题、配图、排版和发布。高效生成适用于各类媒体的定制内容,加速品牌传播,提升内容营销效果。

热门AI辅助写作AI工具讯飞绘文内容运营AI创作个性化文章多平台分发AI助手
材料星

材料星

专业的AI公文写作平台,公文写作神器

AI 材料星,专业的 AI 公文写作辅助平台,为体制内工作人员提供高效的公文写作解决方案。拥有海量公文文库、9 大核心 AI 功能,支持 30 + 文稿类型生成,助力快速完成领导讲话、工作总结、述职报告等材料,提升办公效率,是体制打工人的得力写作神器。

openai-agents-python

openai-agents-python

OpenAI Agents SDK,助力开发者便捷使用 OpenAI 相关功能。

openai-agents-python 是 OpenAI 推出的一款强大 Python SDK,它为开发者提供了与 OpenAI 模型交互的高效工具,支持工具调用、结果处理、追踪等功能,涵盖多种应用场景,如研究助手、财务研究等,能显著提升开发效率,让开发者更轻松地利用 OpenAI 的技术优势。

下拉加载更多