接下来的篇章我们会把目光聚焦於 interfaces/ 这个目录底下的内容,想确认 slate package 完整的 src directory 的读者们可以回到 上一篇 做确认。
我们首先来为它底下的 files 做点简单的分类与介绍:
除了 custom-types.ts 是专门定义与处理 type extension 之外,其他的 file 都各自代表着一组 concept ,也就是上图画了红线的部分。它们各自同时拥有:定义这组 concept 相关的 types 、这组 concept 提供给开发者使用的 method apis。
我们用一个名叫 Example
的范例来讲解 concept file 里基本的 code-structure :
export type ExampleType = {
// type declaration, use "interface" to declare it if it's extendable
}
export interface ExampleInterface {
// Concept method apis interface declaration
}
export const Example: ExampleInterface = {
// Concept methods implementation
}
如果 type 被定义为 extendable 的话,我们则能看到额外引用 custom-types.ts 里的 ExtendedType
type utility 去扩充其基本 BaseType 的 type 。
type 来 type 去的是想乱死谁,直接上个范例最清楚,一样用 Example
来介绍。
import { ExtendedType } from './custom-types';
export interface BaseExample {
// type declaration
}
export type Example = ExtendedType<'Example', BaseExmaple>
要注意其实上面的范例放进 ExtendedType
是不过关的,因为 ExtendedType
吃得第一个 string generic 是有用一个 Union type 限制住的,详情我们後续在 custom-type 的篇章会再深入讲解,目前只是先让读者们有个概念而已。
接着我们看到有被黄框的 files ,这些是我们在建立 editor 与操作 editor value 时主要会使用到的概念,我们接下来会花不少的篇幅,拿官方提供的注解当温开水配着服用,依序介绍里面的 types ,而各个 files 里提供的 method apis 因为数量众多又杂又乱,一条一条介绍完另一个 30 天就又过去了所以我们就不在这边细说,笔者的时间跟心态都算充裕的话再另外整理成 reference 提供给各位吧~
那麽就如标题所言,我们首先会针对 slate 的 document model 所需要用到的 concepts 做介绍,分别是: text 、 element。
这里面就只有一个 Extendable 的 Text type 而已,光看名字应该也能很直觉的反应出它代表的就是 editor 里的『文字资料』。
/**
* `Text` objects represent the nodes that contain the actual text content of a
* Slate document along with any formatting properties. They are always leaf
* nodes in the document tree as they cannot contain any children.
*/
export interface BaseText {
text: string
}
export type Text = ExtendedType<'Text', BaseText>
Text
type 只规定了其中的内容物必须要有一个类别为 string 的 text
property ,开发者可以依照自己的喜好定义其他的 properties ,就拿我们在 Day3 举过官方范例来看看。
下图只要有被红圈圈到的 Object 都各为一组 Text
再来看看它的 module declaration
type CustomText = {
bold?: boolean
italic?: boolean
code?: boolean
text: string
}
// slate module "Text" declaration
declare module 'slate' {
interface CustomTypes {
Text: CustomText
}
}
把定义好的 module 引入就能使用自定义的 CustomText 随意开发了,就是这麽轻松写意。
Text 提供的 method apis 里有一个特别值得提到的 method 是 decorations()
/**
* Get the leaves for a text node given decorations.
*/
decorations(node: Text, decorations: Range[]): Text[] {
... // method implementation
}
Range
的概念我们会在 下一篇 介绍,目前读者先知道它是 Slate 中描述一段可以横跨不同的 Text
甚至 Element
的『字元集范围』就好,一个 Range 里面会同时有 anchor 与 focus 这两个 properties 代表这个 range 的起始字元位置以及结束字元位置。
当我们丢给 decorations 合法的 Text 以及 range list ,也就是这些 Ranges 的范围是正确的位於该 Text 内时,它会动态地帮我们依照这些 Range 拆成一组 Text list。
const text = { text: 'This is a text example.' };
const ranges = [{
anchor: { path: [0, 0], offset: 5 },
focus: { path: [0, 0], offset: 7 },
}];
/**
returns: [
{ text: 'This ' },
{ text: 'is' },
{ text: ' a text example.' },
] */
Text.decorations(text, ranges);
当然你也可以放上自定义的 Text property
const text = { text: 'This is text example2.' };
const ranges = [{
anchor: { path: [0, 0], offset: 5 },
focus: { path: [0, 0], offset: 7 },
bold: true,
}];
/**
returns: [
{ text: 'This ' },
{ text: 'is', bold: true },
{ text: ' text example2.' },
] */
Text.decorations(text, ranges);
嗯?干嘛多此一举?直接修改 document model 里的 value 不就好了吗?
笔者一开始也很困惑於这个功能存在的意义,确实直接修改 value 就能达到一模一样的效果了,後来在官方提供的 Search Highlighting example 里找到了答案:
它很适合用於开发 「修改的频率高,幅度大,却只修正 Text properties 而不会更改到文字的内容或其他 value 的功能」。正如其名 decorations ,它的工用就像是在整个工程的尾端轻轻的装饰文字的属性,因为不会去更动到 editor value ,所以可以省略掉许多更新整个 value tree 庞杂的运算,因此把它放在 view layer 的浅层,动态地计算 decorate 後的 Text ,会让速度整个快上非常非常多。(读者可以自己去实验看看 XD
里面主要定义的 type 也只有一个 Extendable 的 Element
type 而已,slate 的 document model 就是由 Element
与 Text
这两种 types 所组成的,相对於 Text
所代表的文字资料, Element
代表 editor 里的元件资料
/**
* `Element` objects are a type of node in a Slate document that contain other
* element nodes or text nodes. They can be either "blocks" or "inlines"
* depending on the Slate editor's configuration.
*/
export interface BaseElement {
children: Descendant[]
}
export type Element = ExtendedType<'Element', BaseElement>
Element
type 只规定了其中的内容物必须要有一个类别为 children 的 Descendant[]
property ,开发者一样可以依照自己的喜好定义其他的 properties 。
Descendant
type 象徵着 slate document tree 里的子节点,我们会在之後的 node concept 章节 详细介绍,我们现在先偷瞄一眼 Descendant
type 的定义
/**
* The `Descendant` union type represents nodes that are descendants in the
* tree. It is returned as a convenience in certain cases to narrow a value
* further than the more generic `Node` union.
*/
export type Descendant = Element | Text
可以看到 Element
与 Descendant
这两个 type 是彼此关联的,在 Element
的 children
property 里再放上另一个 Element
也是没问题的,这也是为什麽开发者可以很直观地透过 slate 建立拥有巢状结构的 feature ,只要照着 type 的规范走 slate 完全不在乎你的 document tree 长什麽样子,你爱怎麽巢就怎麽巢。
另外我们也可以在 Element
type 的注解里看到,开发者也能针对各个不同的 elements 定义 blocks
或 inlines
属性,这部分因为牵涉到 slate editor 的 config 定义,我们就放到後面介绍 editor concept 的篇章吧!
element.ts 里还有一个附加的 type 是 ElementEntry
,它主要是用在进行 iterate 相关操作的用途上,关於 iterate 我们之後会在整理出一篇文章跟各位分享分享。
哇今天的内容不少诶!来稍微整理一下:我们首先对 interfaces/ 这个目录底下的 files 做了个简单的分类 → 定义画了 红线 的 concept files 里的 code structure → 分别介绍了 Document model 里会出现的
Text
type 以及decorations
methods 、Element
type 以及它与Descendant
type 的关系
感谢你的整理,今天的篇幅有那麽一点长,请读者花些时间消化一下,紧接着到下一篇我们要介绍 slate 是如何实现 document value 的『定位』这件事的。
咱们一样下一篇见啦~
其实RISC-V官方也有开发了一个instruction accurate等级的模拟器Spike,只...
接下来为您介绍 10 款不错的第三方 YouTube 下载软件!让我们来看看哪个软件才是 2022 ...
昨天已经看过我们在实务上可能会遇到的需求,利用多个可能重复范围的配对池,当作匹配搜寻条件,今天让我们...
随着Internet越来越多地使用在商业和个人通讯,保护敏感讯息(例如:信用卡和 个人识别码(PIN...
这个部落格练习难度明显比前一份的难度要提升不少 也使用到一些共用的概念 首先介绍了工厂模式 [烧瓶里...