介绍
- 可扩展的 JavaScript Web 文本编辑器框架
- 原理:一个
contentEditable
的元素
主要概念
Editor instances
- 将所有内容连接在一起的核心
- 使用
createEditor()
创建,使用框架绑定时,@lexical/react
会自动处理
Editor States
- 底层数据模型,包含两部分
- a Lexical node tree
- a Lexical selection object
- 创建之后不可变,需通过
editor.update(() => {...})
更新编辑器状态 - 获取当前编辑器状态:
editor.getEditorState()
- 编辑器状态可以完全序列化为 JSON,再使用
editor.parseEditorState()
序列化回编辑器
Editor Updates
- 当您想要更改编辑器状态中的某些内容时,必须通过更新来完成
editor.update(() => {...})
- 拥有 active editor state 的完整“lexical”上下文
- 公开了对底层编辑器状态节点树的访问
DOM Reconciler
- Lexical 有自己的 DOM Reconciler,它采用一组 Editor States(始终是“current”和“pending”)并对它们应用“diff”。然后,使用此 diff 来仅更新 DOM 中需要更改的部分。
Listeners, Node Transforms and Commands
- 除了调用更新之外,Lexical 完成的大部分工作都是通过 Listeners、Node Transforms 和 Commands 完成的。
const unregisterListener = editor.registerUpdateListener(({editorState}) => {
// An update has occurred!
console.log(editorState);
});
// Ensure we remove the listener later!
unregisterListener();
- Commands 命令是 Lexical 中用于将所有内容连接在一起的通信系统
- Custom commands
- 创建自定义
commands :createCommand()
,并分派到编辑器中:editor.dispatchCommand(command, payload)
- 处理
commands:editor.registerCommand(handler, priority)
- 创建自定义
如何独立于任何框架或库使用 Lexical
Creating an editor and using it
- 编辑器实例可以被认为是负责将 EditorState 与 DOM 连接起来的实例。
- 编辑器也是您可以注册自定义节点、添加侦听器和转换的地方
import {createEditor} from 'lexical';
// 创建编辑器实例
const config = {
namespace: 'MyEditor',
theme: {
...
},
onError: console.error
};
const editor = createEditor(config);
// 将编辑器实例与 a content editable <div> element 关联起来
const contentEditableElement = document.getElementById('editor');
editor.setRootElement(contentEditableElement);
// 从元素中清除编辑器实例
// editor.setRootElement(null)
Working with Editor States
- 调用
editor.getEditorState()
获取编辑器状态,也可对其序列化和反序列化
const stringifiedEditorState = JSON.stringify(editor.getEditorState().toJSON());
const newEditorState = editor.parseEditorState(stringifiedEditorState);
Updating an editor
有几种方法可以更新编辑器实例(异步过程):
editor.update()
editor.setEditorState()
editor.registerNodeTransform()
editor.registerCommand(EXAMPLE_COMMAND, () => {...}, priority)
import {$getRoot, $getSelection, $createParagraphNode, $createTextNode} 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);
});
如果您想知道编辑器何时更新以便对更改做出反应,可以向编辑器添加更新侦听器,如下所示:
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.
});
});