9.6K Star!可擴(kuò)展的富文本編輯框架!
簡(jiǎn)介
Lexical 是一個(gè)可擴(kuò)展的 JavaScript 網(wǎng)頁(yè)文本編輯器框架,強(qiáng)調(diào)可靠性、可訪問(wèn)性和高性能,旨在提供一流的開發(fā)人員體驗(yàn),輕松地制作原型并充滿信心的構(gòu)建功能。結(jié)合高度可擴(kuò)展的架構(gòu),Lexical 允許開發(fā)人員創(chuàng)建在大小和功能上可擴(kuò)展的獨(dú)特文本編輯體驗(yàn)。
Lexical 可以輕松創(chuàng)建復(fù)雜的文本編輯體驗(yàn),否則使用內(nèi)置瀏覽器工具會(huì)非常復(fù)雜,比如可以用來(lái)構(gòu)建這些:
- 比 a 標(biāo)簽有更多要求的簡(jiǎn)單純文本編輯器 textarea,例如定義表情符號(hào)、鏈接、主題標(biāo)簽等功能
- 更復(fù)雜的富文本編輯器,可用于在博客、社交媒體、消息傳遞應(yīng)用程序上發(fā)布內(nèi)容
- 一個(gè)成熟的 WYSIWYG 編輯器,可用于 CMS 或富文本編輯器
- 結(jié)合上述特點(diǎn)的實(shí)時(shí)協(xié)作文本編輯
Lexical 具有以下特點(diǎn):
- 可靠性。Lexical 由每個(gè)附加到單個(gè)內(nèi)容可編輯元素的編輯器實(shí)例組成,一組編輯器狀態(tài)代表編輯器在任何給定時(shí)間的當(dāng)前和待定狀態(tài)。
- 可訪問(wèn)性。Lexical 遵循 WCAG 中建立的最佳實(shí)踐,并與屏幕閱讀器和其他輔助技術(shù)兼容
- 效率高。它不直接關(guān)注 UI 組件、工具欄或富文本功能和 markdown,這些功能的邏輯可言通過(guò)插件接口包含在內(nèi)
支持以下瀏覽器:
- Firefox 52+
- Chrome 49+
- Edge 79+
- Safari 11+
- iOS 11+ (Safari)
- iPad OS 13+ (Safari)
- Android Chrome 72+
項(xiàng)目地址:
https://github.com/facebook/lexical
幾個(gè)重要的概念
編輯器實(shí)例
編輯器實(shí)例是將所有東西連接在一起的核心,可以將 contenteditable DOM 元素附加到編輯器實(shí)例上,注冊(cè)和監(jiān)聽命令,更重要的是,編輯器允許更新其自身的 EditorState。
// 創(chuàng)建編輯器實(shí)例
createEditor()
編輯器狀態(tài)
編輯器狀態(tài)表示你希望在 DOM 上顯示的內(nèi)容的底層數(shù)據(jù)模型,包含兩部分:
- Lexical 節(jié)點(diǎn)樹
- Lexical 選定器對(duì)象。編輯器狀態(tài)一旦創(chuàng)建就不可更改,可以使用以下方法進(jìn)行創(chuàng)建:
editor.update(() => { })
可以將節(jié)點(diǎn)轉(zhuǎn)換或命令處理程序作為鉤子掛到 update 方法中,它們將作為更新流程的一部分而被調(diào)用,以防止更新的級(jí)聯(lián)/瀑布式更新。
// 獲取當(dāng)前編輯器狀態(tài)
editor.getEditorState()
// 編輯器狀態(tài)可以被序列化成 JSON,通過(guò)該方法將 JSON 轉(zhuǎn)換回實(shí)例對(duì)象
editor.parseEditorState()
編輯器更新
如果想要更改編輯器狀態(tài)中的某些內(nèi)容,必須通過(guò)更新操作來(lái)完成:
editor.update(() => { })
傳遞給更新調(diào)用的閉包很重要,這是一個(gè)擁有活動(dòng)中編輯器狀態(tài)完整上下文的地方,它公開了對(duì)底層編輯器狀態(tài)節(jié)點(diǎn)樹的訪問(wèn)。
DOM 協(xié)調(diào)器
Lexical 有自己的 DOM 協(xié)調(diào)器,它采用一組編輯器狀態(tài)(總是“當(dāng)前”和“待定”)并在它們上應(yīng)用“差異”。然后它使用此差異僅更新 DOM 中需要更改的部分,可以將其視為一種虛擬 DOM,使得 Lexical 能夠跳過(guò)許多差異比較工作,因?yàn)樗涝诮o定更新中發(fā)生了哪些變化。
監(jiān)聽器、節(jié)點(diǎn)轉(zhuǎn)換和命令
除了調(diào)用更新之外,使用 Lexical 完成的大部分工作是通過(guò)監(jiān)聽器、節(jié)點(diǎn)轉(zhuǎn)換和命令完成的。這些操作都來(lái)自編輯器實(shí)例,以 register 開頭。另一個(gè)重要特性是所有注冊(cè)方法都返回一個(gè)函數(shù)以輕松取消訂閱它們。
const unregisterListener = editor.registerUpdateListener(({editorState}) => {
console.log(editorState);
});
unregisterListener();
命令是用于將 Lexical 中的所有內(nèi)容鏈接到一起的通信系統(tǒng),當(dāng)按鍵被觸發(fā)或其他重要信號(hào)出現(xiàn)時(shí),語(yǔ)法在內(nèi)部調(diào)度命令,傳入的命令按優(yōu)先級(jí)通過(guò)所有處理程序進(jìn)行傳播,類似于瀏覽器的時(shí)間傳播機(jī)制
// 創(chuàng)建自定義命令
createCommand()
// 發(fā)送到編輯器實(shí)例
editor.dispatchCommand(command, payload)
// 也可以直接注冊(cè)命令
editor.registerCommand(handler, priority)
一個(gè)簡(jiǎn)單的例子
這個(gè)例子可以用來(lái)創(chuàng)建一個(gè)簡(jiǎn)單的富文本編輯器:
import "./styles.css";
import Editor from "./Editor";
export default function App() {
return (
<div className="App">
<h1>Rich Text Example</h1>
<p>Note: this is an experimental build of Lexical</p>
<Editor />
<div className="other">
<h2>Other Examples</h2>
<ul>
<li>
<a
href="https://codesandbox.io/s/lexical-plain-text-example-g932e"
target="_blank"
rel="noreferrer"
>
Plain text example
</a>
</li>
</ul>
</div>
</div>
);
}