這一篇是這一系列 Libraries 比較文實質性的最後一篇了,在下一篇稍做總結以後接著我們就要正式進入到 source-code 解析的篇章了!
這篇我們主要會圍繞在 Slate 在官網首頁提到的 Principles 來介紹它的特色,為了方便讀者閱讀,我們還是上個截圖(單押 x1
我們先從第 1. 2. 7. 這三點開始說明起:
Slate 專案是搭配 Lerna 建立的 mono-repo,它將它的核心模型(core model)獨立出來,自成一個 package
上圖就是 Slate 官方完整提供給開發者使用的套件包們,slate package 就是存放著核心模型的套件包,而 slate-react 則是官方提供的 react view layer,這也是我們在上一篇提到過,可以在不同的框架下自己建立的 slate view layer 的原因。
另外 Slate 完全使用 Typescript 開發,基本上所有他提供給你的工具、概念都定義好了一組 extendable 的 base type 讓開發者去使用甚至擴充,他提供給開發者的都是底層核心所需用到的最基本的邏輯或是最基本的 use-case 下所需要的工具與 methods,為的都是提供開發者可完全客製化的環境,這同時也是 Slate 自認最重要的特色。
直接來看官方提供,覆蓋 editor 的核心 properties 的範例就能深刻感受到他們追求極致客製化的決心了
interface Editor {
children: Node[]
selection: Range | null
operations: Operation[]
marks: Record<string, any> | null
// Schema-specific node behaviors.
isInline: (element: Element) => boolean
isVoid: (element: Element) => boolean
normalizeNode: (entry: NodeEntry) => void
onChange: () => void
// Overrideable core actions.
addMark: (key: string, value: any) => void
apply: (operation: Operation) => void
deleteBackward: (unit: 'character' | 'word' | 'line' | 'block') => void
deleteForward: (unit: 'character' | 'word' | 'line' | 'block') => void
deleteFragment: () => void
insertBreak: () => void
insertFragment: (fragment: Node[]) => void
insertNode: (node: Node) => void
insertText: (text: string) => void
removeMark: (key: string) => void
}
? 是 editor 的 base interface。補充說明一下 editor 就是 slate 提供給開發者開發編輯器時的操作器,比如: children
裡存放著主要資料、 selection
裡存放著使用者在頁面上的反白區域、 normalizeNode
提供對節點進行正規化的方法、 addMark, apply
... 等等的是提供給開發者操作編輯器資料的方法之一。
更詳細的介紹放在之後的篇章,目前讀者先對它大致有個輪廓就好了。
const { isInline } = editor
editor.isInline = element => {
return element.type === 'link' ? true : isInline(element)
}
? 接著是 override base editor 中 "isInline" 這個 property 的範例。
import { createEditor } from 'slate'
const withImages = editor => {
const { isVoid } = editor
editor.isVoid = element => {
return element.type === 'image' ? true : isVoid(element)
}
return editor
}
const editor = withImages(createEditor())
? 甚至你也能選擇透過 "plugin" 的方式在建立 editor 時覆寫它的 property
幾乎所有我們在 slate package 裡看到的一切 methods 甚至資料本身都能輕易地擴充,一些試圖擴充所容易導致的 error case 它們也會特別註明,可謂是花費了不小的心力。
接著我們來看看 3.,這邊的 document model 指的就是 slate 的資料層,我們曾在 Day3 附上範例圖,這邊再重放一次幫讀者回憶一下,也方便我們介紹
我們會在後續深入解析的章節時再詳細介紹,目前先記得結論就好:Slate 採用的 document model (也是開發者主要要操作的資料)是一個近似於 DOM ,巢狀且遞迴的樹狀結構。只要符合格式規範,它完全不在乎你在 document 裡放了哪些資料,以上圖為例:
children
同層的 siblings 被歸類為 Element
type,只要有符合規範的 children
欄位,其他欄位都是自定義的,你可以把 type
這個欄位換成別的名字、別的類別,甚至刪掉或新增別的欄位也都沒問題。text
同層的 siblings 被歸類為 Text
type,只要有符合規範的 text
欄位,其他欄位也都是自定義的,諸如 bold
、 italic
、code
這些欄位也都是隨便你改,想刪掉或增加新的也都沒問題,直接想成是 DOM attribute 能更快理解一些。Element
type 的 children
欄位底下再放上另外一個 Element
type 也是沒問題的也因為這個特性讓它在建立 table 或是 nested block quotes 等需要二維以上資料結構的功能時相較於 Draft 顯得特別直觀。
額外補充 Slate 也內建支援對 HTML 的 serialization 與 de-serialization,這也是歸功於這種資料模型讓這件事能輕易達成。
再來是 4. ,Slate 的主要設計原則之一就是要平行於 DOM 開發,它由裡到外都被設計地與 DOM 非常相近,為的就是讓開發者在開發時能更直觀些。
也因此能經常從中挖掘出與 DOM 相似的概念,如: selections 、 ranges 、 offset ... 等等,除此之外無論是 document 設計為 nested tree,再到提供的 methods api 讓開發者能各種 access data model ,以及標準的 event handlers 都能在 Slate 中使用,都能看出 Slate 以 DOM 為目標的設計理念,一言以敝之就是基本上在 DOM 上你能做到的任何事,你都能在 Slate 中做到。
這也讓它的學習曲線不會到真的那麼抖,雖然操作上與 DOM 的差異依舊存在,但你仍可以以 DOM 的角度去理解與學習,而不用瘋狂爬文或開腦洞去學習全新的概念
剩下的 5. 讀者可以自行去它的 source-code 意會一下 XD
再來還是要聊一下缺點,維持公平公正客觀。
主要就是『成熟度』這點上面:
Slate 目前仍在 beta 版本,光是這點就完全無法與其他 libraries 的成熟度作比較了,test coverage 不高,github 上也能看到不少 edge cases 的 issues ,大部分遇到的問題要馬自己去 source code 裡找答案查 command,不然就是在 issues 跟尚未被 merge 的 pr 裡找答案,很多實際踩過雷以後才會釐清的問題存在,而這對於尋求專案穩定度的人來說可能就不太適合。(哪天突然宣布個 breaking changes 工程師就準備加班了 ?
本篇的特色介紹就到此為止,希望能多少引起讀者們對它的興趣,願意跟我一起深入其中一虧究竟XD
Slate 所提供的功能當然不只如此,我們也會在後續逐步解析 source-code 的過程中慢慢挖掘。
咱們一樣下一篇文章見~