Lexical 概念

介紹

  • 可擴展的 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
    1. 創建自定義 commands :createCommand() ,并分派到編輯器中:editor.dispatchCommand(command, payload)
    2. 處理 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.
  });
});
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容