Day 10. slate × 架构蓝图

https://ithelp.ithome.com.tw/upload/images/20210925/20139359VX8Zaa1bH9.png

在开始今天的主题前,虽然在前面介绍 Slate 时已经有稍微提到过了,我们还是先从 slate 的各个 packages 分别负责的领域开始介绍起。

https://ithelp.ithome.com.tw/upload/images/20210925/20139359QCjGFcjiJs.png

  • slate:Slate 的资料层 package ,负责一切与核心模型相关的逻辑处理,也是本篇章的主角。
  • slate-history:提供 Slate 支援 undo/redo 功能的 package 。
  • slate-hyperscript:协助 Slate 进行 JSX Deserializing 功能的 package ,感兴趣的读者可以到官方文件的 Serializing 页面一探究竟
  • slate-react:官方提供的 react view layer 的 slate implemetation 。

slate Directory


首先第一步先从确定工作量开始,我们直接来看 slate package 的 src 之下的 directory 吧!

https://ithelp.ithome.com.tw/upload/images/20210925/20139359MeslayaotS.png

咦?这个量怎麽跟我预期的不太一样,它不是负责处理整个核心逻辑的吗?应该要多一点才对吧?


这就是 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 。

    childrenselectionmarksoperations 储存了整个 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


slate Process


接着我们上个简单的 whimsical 图来看看 Slate 的基本运作概念:

https://ithelp.ithome.com.tw/upload/images/20210925/20139359tct3vu7wnD.png

  1. 我们首先从 createEditor 函式取得 editor ,并主要透过 Transform methods 操作 editor state。
  2. Transform 的底层会再经过一系列的 Operations 更新 Editor values ,其中 childrenselection 会再经过一层 Immer 的包装做转换。
  3. 等到一系列的 Operation 完成, Slate 标示 " Finish Flushing " 後再触发 onChange function 。

如果我们试着省略中间较为繁琐的过程,将它们概括为一个周期产生一组的 Actions (行为)的话,不难发现它其实就是一组经典的单向资料流的模型。

https://ithelp.ithome.com.tw/upload/images/20210925/20139359DLJ4ckRten.png

这就是最基本、简化过後的 slate 运作模型,先让读者有个画面,接下来我们会将目光聚焦於 interfaces/ ,那我们就一样下一篇见啦!


<<:  找LeetCode上简单的题目来撑过30天啦(DAY10)

>>:  Day 22.5 | Livewire 实作 购物网站: 建立资料表

16. 从Code review体现公司文化

前言 这篇有两个主题:公司文化与code review,而讲者特别强调的是要如何将这两件事情中间做...

[13th][Day29] node

使用 k8s 的过程中,node , pod RC , service 等概念都可以看作是一种资源,...

应用 LINE Front-end Framework 轻松建立互动 (1)

我们的验证码小帮手现在可以完成的项目有: 对使用者进行身份验证与绑定 判断使用者的讯息,进而回覆对应...

浅谈注册Google戈 Play Developer

若要发布开发的软件到Google Play,要先成为Google Play Developer。成为...