介紹
- 可擴展的 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.
});
});