在开始今天的主题前,虽然在前面介绍 Slate 时已经有稍微提到过了,我们还是先从 slate 的各个 packages 分别负责的领域开始介绍起。
首先第一步先从确定工作量开始,我们直接来看 slate package 的 src 之下的 directory 吧!
咦?这个量怎麽跟我预期的不太一样,它不是负责处理整个核心逻辑的吗?应该要多一点才对吧?
这就是 Schema-less 这项特性的美妙之处了,我们在 Day8 的时候也有提到过 Slate 本质上是提供给开发者一个 full-customizable 的 DOM like editor , 它不会猜测各种复杂的使用情境,只提供给开发者最基本的 concepts 以及 method-apis,也因此它的工作量比一般的 libraries 要少上许多。
我们先为上图的第一层 folders 与 file 区分他们所负责的功能:
interfaces/ :定义了所有 slate 使用到的概念的 Base type(同时也包含 BaseEditor
的 interface 以及 low-level action 的 Operation types),以及这些概念所提供的 method apis 。
transforms/ : Transforms 、 Operations 为在新版 Slate 的世界中,二个主要操作 immutable editor 的方法,让开发者可以透过它们来自由操作 editor state 。
其实 Slate 里还有一个词叫 "Command" ,在新、旧版的 Slate 中都有出现,却代表不同的意义,笔者一开始也被这几个词搞得晕头转向。
旧版 Slate 的 Command 其实就是新版的 Transforms ,他们的用途一样是提供给开发者操作 editor 的 methods ,只是新版另外整理成一套更完整的介面给开发者使用,他们本质上是一样的。
新版 Command 的重点则是 "Custom" 这一词,是提供给开发者搭配 Transforms 与 Operations 去制作贴近於自己创造的编辑器 use-case 的 reusable function ,官方文件上是将它视为 high-level actions ,但其实看起来就是提供一个名义让开发者自由搭配 Transforms 与 Operations 去建立自己的 helper function ,可能留个名字给它看起来比较炫炮吧 XD
附上 官方文件 上提供的写法,读者可以先加减看一下,我们在之後介绍到 interface/editor.ts 的文章时会再提到它。
import { Editor } from 'slate';
const MyEditor = {
...Editor,
insertParagraph(editor) {
// ...
},
}
utils/ :一些辅助 slate package 内部使用的函数
create-editor.ts :提供负责 build 并 return immutable editor 的 create function ,而 function 里做的事就是初始化 editor 里头的资料,包含 data model ,以及各种 behaviors 与 actions 。
来偷瞄一眼 interfaces/editor.ts
里 BaseEditor 的 interface 是怎麽定义的
export interface BaseEditor {
children: Descendant[]
selection: Selection
operations: Operation[]
marks: Omit<Text, 'text'> | 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: (direction?: 'forward' | 'backward') => void
getFragment: () => Descendant[]
insertBreak: () => void
insertFragment: (fragment: Node[]) => void
insertNode: (node: Node) => void
insertText: (text: string) => void
removeMark: (key: string) => void
}
这就是 slate 回传给开发者的 editor state 的 interface 。
children
、 selection
、 marks
、 operations
储存了整个 slate editor 需要的资料,我们会在 Data-Model 的篇章 深入介绍。
再来有个值得注意的是 onChange
这个 function , 在执行 Transform methods 时, Slate 其实会在内部触发多次的 Operations 分次去修改 Editor value ,如果我们只纯粹以 State change 作为画面 re-render 的判断依据的话就会导致『一次 Transform 就伴随着多次不必要的 re-render』,这当然不是我们所乐见的, Slate 也知道我们不喜欢,於是它利用了 Promise Micro-Task 制作了 FLUSHING 机制,提供 onChange
这个 overrideable 的 function 让开发者能在正确的时机点 re-render 画面。
其余的则为官方提供的 build-in methods ,开发者可以搭配使用这些 Overrideable actions 去建立 Command
。
接着我们上个简单的 whimsical 图来看看 Slate 的基本运作概念:
createEditor
函式取得 editor ,并主要透过 Transform methods 操作 editor state。children
与 selection
会再经过一层 Immer 的包装做转换。onChange
function 。如果我们试着省略中间较为繁琐的过程,将它们概括为一个周期产生一组的 Actions (行为)的话,不难发现它其实就是一组经典的单向资料流的模型。
这就是最基本、简化过後的 slate 运作模型,先让读者有个画面,接下来我们会将目光聚焦於 interfaces/ ,那我们就一样下一篇见啦!
<<: 找LeetCode上简单的题目来撑过30天啦(DAY10)
>>: Day 22.5 | Livewire 实作 购物网站: 建立资料表
前言 这篇有两个主题:公司文化与code review,而讲者特别强调的是要如何将这两件事情中间做...
使用 k8s 的过程中,node , pod RC , service 等概念都可以看作是一种资源,...
我们的验证码小帮手现在可以完成的项目有: 对使用者进行身份验证与绑定 判断使用者的讯息,进而回覆对应...
若要发布开发的软件到Google Play,要先成为Google Play Developer。成为...
...