2024 前端頂級 WYSIWYG 富文本編輯器 Slate 火了!

2024年2月6日 21点热度 0人点赞

傢好,很高興又見面了,我是"高級前端‬進階‬",由我帶著大傢一起關註前端前沿、深入前端底層技術,大傢一起進步,也歡迎大傢關註、點贊、收藏、轉發!

什麼是 Slate

Slate 可讓開發者構建功能豐富、直觀的編輯器,例如: Medium、Dropbox Paper 或 Google Docs 中的編輯器,而代碼庫不會陷入復雜性的泥潭。

Slate 可以輕松做到這一點,因為所有邏輯都是通過一系列插件實現的,因此永遠不會受到 “核心” 中包含或不包含的內容的限制。 開發者可以將其視為構建在 React 之上的 contenteditable 的可插入實現,靈感來自於 Draft.js、Prosemirror 和 Quill 等庫。

Slate 的設計原則如下:

  • 一流的插件:Slate 最重要的原則是“插件是一流的實體”,這意味著可以完全自定義編輯體驗,構建像 Medium 或 Dropbox 的復雜編輯器,而不必違背庫的假設。
  • 無架構核心:Slate 的核心邏輯對將要編輯的數據的模式做了很少的假設,這意味著庫中沒有任何假設,當需要超越最基本的用例時,這些假設會讓開發者陷入困境。
  • 嵌套文檔模型:Slate 使用的文檔模型是一個嵌套的遞歸樹,就像 DOM 本身一樣。 這意味著可以為高級用例創建復雜的組件,例如:表或嵌套塊引用。 但僅使用單個層次結構也很容易保持簡單。
  • 與 DOM 並行: Slate 的數據模型基於 DOM,文檔是一個嵌套樹,使用選擇(Selection)和范圍(Range),並且公開所有標準事件處理程序,這意味著表或嵌套塊引用等高級行為是可能的。 幾乎任何可以在 DOM 中執行的操作,都可以在 Slate 中執行。
  • 直觀的命令:Slate 文檔是使用 “命令” 進行編輯的,這些命令被設計為高級且極其直觀寫入和讀取,以便自定義功能盡可能具有表現力,從而極大地提高了推理代碼的能力。
  • 協作就緒的數據模型:Slate 使用的數據模型(特別是如何將操作應用於文檔)旨在允許在頂層進行協作編輯,因此,如果決定讓編輯器進行協作,則無需重新考慮所有事情。
  • 明確 “核心” 界限: 借助插件優先的架構和無模式核心,“核心” 和 “自定義” 之間的界限變得更加清晰,這意味著核心體驗不會陷入邊緣情況。

目前 Slate 在 Github 通過 MIT 協議開源,有超過 28.6k 的 star、3.2k 的 fork、158k 的項目依賴量、代碼貢獻者 560 、妥妥的前端頂級開源項目。

如何使用 Slate

安裝和 Slate 基礎使用

Slate 是一個 monorepo,分為多個 npm 包,因此要安裝隻需要執行以下操作:

yarn add slate slate-react

還需要確保安裝 Slate 的 peer 依賴項:

yarn add react react-dom

請註意,如果更願意使用 Slate 的預打包版本可以使用 yarn 添加 slate 並檢索打包的 dist/slate.js 文件! 安裝 Slate 後,需要手動導入:

// Import React dependencies.
import React, {useState} from 'react'
// Import the Slate editor factory.
import {createEditor} from 'slate'
// Import the Slate components and React plugin.
import {Slate, Editable, withReact} from 'slate-react'

使用導入之前從一個空的 App 開始:

// Define our app...
const App = () => {
  return null
}

下一步是創建一個新的編輯器對象。 因為希望編輯器在渲染過程中保持穩定,因此使用不帶 setter 的 useState 鉤子:

const App = () => {
  // Create a Slate editor object that won't change across renders.
  const [editor] = useState(() => withReact(createEditor()))
  return null
}

當然,此時還沒有渲染任何東西,所以不會看到任何變化。值得一提的是,如果使用 TypeScript 還需要使用 ReactEditor 擴展編輯器,並根據 TypeScript 文檔添加註釋。 下面的示例還包括本示例其餘部分所需的自定義類型。

// TypeScript users only add this code
import {BaseEditor, Descendant} from 'slate'
import {ReactEditor} from 'slate-react'
type CustomElement = {type: 'paragraph'; children: CustomText[] }
type CustomText = {text: string}
declare module 'slate' {
  interface CustomTypes {
    Editor: BaseEditor & ReactEditor
    Element: CustomElement
    Text: CustomText
  }
}

接下來是渲染 <Slate> 上下文提供程序。

提供程序組件會跟蹤 Slate 編輯器、插件、value、selection 以及發生的任何更改, 同時必須渲染在任何 <Editable> 組件之上。 但也可以使用 useSlate hooks 向其他組件(如工具欄、菜單等)提供編輯器狀態。

const initialValue = [
  {
    type: 'paragraph',
    children: [{text: 'A line of text in a paragraph.'}],
  },
]
const App = () => {
  const [editor] = useState(() => withReact(createEditor()))
  // Render the Slate context.
  return <Slate editor={editor} initialValue={initialValue} />
}

可以將 <Slate> 組件視為為其下面的每個組件提供上下文。

Slate Provider 的 “value” 屬性僅用作 editor.children 的初始狀態。 如果代碼依賴於替換 editor.children 應該直接替換,而不是依賴 “value” 屬性來執行此操作。

這是與 <input> 或 <textarea> 等內容略有不同的思維模型,因為富文本文檔更加復雜,經常需要在可編輯內容旁邊包含工具欄、實時預覽或其他復雜組件。通過共享上下文,其他組件可以執行命令、查詢編輯器的狀態等。

下一步是渲染 <Editable> 組件本身, 該組件的作用類似於 contenteditable。 無論在何處渲染,其都會為最近的編輯器上下文渲染可編輯的富文本文檔。

const initialValue = [
  {
    type: 'paragraph',
    children: [{text: 'A line of text in a paragraph.'}],
  },
]
const App = () => {
  const [editor] = useState(() => withReact(createEditor()))
  return (
    // Add the editable component inside the context.
    <Slate editor={editor} initialValue={initialValue}>
      <Editable />
    </Slate>
  )
}

Slate 添加事件

以上示例已經安裝了 Slate 並在頁面上渲染,當用戶輸入內容可以看到更改, 但有時候想要做的不僅僅是鍵入純文本字符串。

Slate 的偉大之處在於其非常容易定制,就像習慣的其他 React 組件一樣,Slate 允許傳入在某些事件上觸發的處理程序。下面使用 onKeyDown 處理程序在按下某個鍵時更改編輯器的內容。

const initialValue = [
  {
    type: 'paragraph',
    children: [{ text: 'A line of text in a paragraph.' }],
  },
]
const App = () => {
  const [editor] = useState(() => withReact(createEditor()))
  return (
    <Slate editor={editor} initialValue={initialValue}>
      <Editable
        // Define a new handler which prints the key that was pressed.
        onKeyDown={event => {
          console.log(event.key)
        }}
      />
    </Slate>
  )
}

此時當在編輯器中按下某個鍵時,其相應的鍵碼將打印在控制臺中。

本文總結

本文主要和大傢介紹 Slate 富文本編輯器,其可讓開發者構建功能豐富、直觀的編輯器,例如: Medium、Dropbox Paper 或 Google Docs 中的編輯器,而代碼庫不會陷入復雜性的泥潭。因為篇幅問題,關於 Slate 主題隻是做了一個簡短的介紹,但是文末的參考資料提供了大量優秀文檔以供學習,如果有興趣可以自行閱讀。如果大傢有什麼疑問歡迎在評論區留言。

參考資料

https://github.com/ianstormtaylor/slate

https://docs.slatejs.org/walkthroughs/01-installing-slate

https://vrite.io/blog/best-js-rich-text-editors-for-2023/