Lexical是一个可扩展的JavaScript Web文本编辑器框架,强调可靠性,可访问性和性能。Lexical 旨在提供一流的开发人员体验,以便你可以放心地轻松构建原型和构建功能。结合高度可扩展的体系结构,Lexical 允许开发人员创建可扩展大小和功能的独特文本编辑体验。
有关 Lexical 的文档和详细信息,请务必访问 Lexical 网站。
以下是你可以使用 Lexical 执行哪些操作的一些示例:
注意:Lexical 不仅限于 React。一旦创建了任何基于 DOM 的底层库的绑定,Lexical 就可以支持该库。
安装并:
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';
const theme = {
// Theme styling goes here
// ...
}
// When the editor changes, you can get notified via the
// LexicalOnChangePlugin!
function onChange(editorState) {
editorState.read(() => {
// Read the contents of the EditorState here.
const root = $getRoot();
const selection = $getSelection();
console.log(root, selection);
});
}
// Lexical React plugins are React components, which makes them
// highly composable. Furthermore, you can lazy load plugins if
// desired, so you don't pay the cost for plugins until you
// actually use them.
function MyCustomAutoFocusPlugin() {
const [editor] = useLexicalComposerContext();
useEffect(() => {
// Focus the editor when the effect fires!
editor.focus();
}, [editor]);
return null;
}
// Catch any errors that occur during Lexical updates and log them
// or throw them as needed. If you don't throw them, Lexical will
// try to recover gracefully without losing user data.
function onError(error) {
console.error(error);
}
function Editor() {
const initialConfig = {
namespace: 'MyEditor',
theme,
onError,
};
return (
<LexicalComposer initialConfig={initialConfig}>
<PlainTextPlugin
contentEditable={<ContentEditable />}
placeholder={<div>Enter some text...</div>}
/>
<OnChangePlugin onChange={onChange} />
<HistoryPlugin />
<MyCustomAutoFocusPlugin />
</LexicalComposer>
);
}
Lexical 的核心是一个无依赖的文本编辑器框架,它允许开发人员构建功能强大、简单和复杂的编辑器图面。Lexical有几个值得探索的概念:
编辑器实例是将所有内容连接在一起的核心内容。你可以将内容可编辑的 DOM 元素附加到编辑器实例,还可以注册侦听器和命令。最重要的是,编辑器允许对其.你可以使用API创建编辑器实例,但是在使用框架绑定时通常不必担心,例如为你处理的。
EditorState
createEditor()
@lexical/react
编辑器状态是基础数据模型,表示要在 DOM 上显示的内容。编辑器状态包含两个部分:
编辑器状态在创建后是不可变的,为了创建一个编辑器状态,你必须通过 。但是,你也可以使用节点转换或命令处理程序“挂接”到现有更新中 - 这些处理程序作为现有更新工作流的一部分调用,以防止更新的级联/瀑布。你可以使用 检索当前编辑器状态。
editor.update(() => {...})
editor.getEditorState()
编辑器状态也可以完全序列化为 JSON,并且可以使用 轻松地序列化回编辑器。
editor.parseEditorState()
如果要更改编辑器状态中的某些内容,则必须通过更新 .传递给更新调用的关闭很重要。在这里,你可以拥有活动编辑器状态的完整“词法”上下文,并公开对基础编辑器状态的节点树的访问权限。我们提倡在此上下文中使用前缀函数,因为它表示可以专门使用它们的地方。尝试在更新之外使用它们将触发运行时错误,并显示相应的错误。对于那些熟悉 React Hooks 的人来说,你可以认为这些钩子具有类似的功能(除了函数可以按任何顺序使用)。
editor.update(() => {...})
$
$
Lexical有自己的DOM协调器,它采用一组编辑器状态(始终是“当前”和“待定”),并对它们应用“diff”。然后,它使用此差异仅更新 DOM 中需要更改的部分。你可以把它看作是一种虚拟的DOM,除了Lexical能够跳过做大部分的差异工作,因为它知道在给定的更新中发生了什么变化。DOM 协调器采用性能优化,有利于内容可编辑的典型启发式方法,并能够自动确保 LTR 和 RTL 语言的一致性。
除了调用更新之外,使用 Lexical 完成的大部分工作是通过侦听器、节点转换和命令完成的。这些都源于编辑器,并以 为前缀。另一个重要功能是,所有 register 方法都返回一个函数来轻松取消订阅它们。例如,以下是你收听词法编辑器更新的方式:
register
const unregisterListener = editor.registerUpdateListener(({editorState}) => {
// An update has occurred!
console.log(editorState);
});
// Ensure we remove the listener later!
unregisterListener();
命令是用于在 Lexical 中将所有内容连接在一起的通信系统。可以使用 创建自定义命令,并使用 将 命令分派给编辑器。当触发按键和其他重要信号发生时,Lexical 在内部调度命令。也可以使用 来处理命令,并且传入的命令按优先级通过所有处理程序传播,直到处理程序停止传播(类似于浏览器中的事件传播)。
createCommand()
editor.dispatchCommand(command, payload)
editor.registerCommand(handler, priority)
本节介绍如何独立于任何框架或库使用 Lexical。对于那些打算在他们的 React 应用程序中使用 Lexical 的人来说,建议查看 @lexical/react 中提供的钩子的源代码
。
使用 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)
更新编辑器的最常见方法是使用 。调用此函数需要传入一个函数,该函数将提供更改基础编辑器状态的访问权限。启动全新更新时,将克隆当前编辑器状态并将其用作起点。从技术角度来看,这意味着 Lexical 在更新期间利用了一种称为双缓冲的技术。有一个编辑器状态表示屏幕上的当前内容,另一个正在进行的编辑器状态表示将来的更改。
editor.update()
创建更新通常是一个异步过程,它允许 Lexical 在单个更新中将多个更新一起批处理,从而提高性能。当 Lexical 准备好将更新提交到 DOM 时,更新中的基础突变和更改将形成新的不可变编辑器状态。然后,调用将根据更新中的更改返回最新的编辑器状态。
editor.getEditorState()
下面是如何更新编辑器实例的示例:
import {$getRoot, $getSelection, $createParagraphNode} from 'lexical';
// Inside the `editor.update` you can use special $ prefixed helper functions.
// These functions cannot be used outside the closure, and will error if you try.
// (If you're familiar with React, you can imagine these to be a bit like using a hook
// outside of a React function component).
editor.update(() => {
// Get the RootNode from the EditorState
const root = $getRoot();
// Get the selection from the EditorState
const selection = $getSelection();
// Create a new ParagraphNode
const paragraphNode = $createParagraphNode();
// Create a new TextNode
const textNode = $createTextNode('Hello world');
// Append the text node to the paragraph
paragraphNode.append(textNode);
// Finally, append the paragraph to the root
root.append(paragraphNode);
});
如果你想知道编辑器何时更新,以便对更改做出 React ,则可以向编辑器添加更新侦听器,如下所示:
editor.registerUpdateListener(({editorState}) => {
// The latest EditorState can be found as `editorState`.
// To read the contents of the EditorState, use the following API:
editorState.read(() => {
// Just like editor.update(), .read() expects a closure where you can use
// the $ prefixed helper functions.
});
});
克隆此存储库
安装依赖项
npm install
启动本地服务器并运行测试
npm run start
npm run test-e2e:chromium仅运行铬 e2e 测试
npm run start将同时启动开发服务器和协作服务器。如果不需要协作,请使用仅启动开发服务器。
npm run dev
下载并安装 VSCode
安装扩展
注意:Lexical 不支持 Internet Explorer 或旧版 Edge。
git checkout -b my-new-branch
git commit -a -m 'Description of the changes'
git push origin my-new-branch
如果你对Lexical有任何疑问,想讨论错误报告,或对新集成有疑问,请随时将自己添加到我们的Discord服务器。
词法工程师会定期检查这一点。
npm run test-unit仅运行单元测试。
npm run test-e2e:chromium仅运行铬 e2e 测试。
npm run debug-test-e2e:chromium在磁头模式下仅运行铬 e2e 测试以进行调试。
npm run test-e2e: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 许可的。