# 接口
Slate
使用纯 JSON
对象。它要求 JSON
对象符合某些接口。比如,一个 Slate
的文本节点必须遵守 Text
接口:
interface Text {
text: string
[key: string]: unknown
}
2
3
4
这就意味着它必须有一个文本内容的 text
属性。
但是也可以添加任何其他的自定义属性,这完全取决于你。你可以为特殊用例和域,添加任何你喜欢的格式逻辑来定制数据,而不会受到 Slate
的干扰。
这种基于接口的办法将 Slate
和大多数其他要求使用它们 hand-rolled
模型的富文本编辑器分开,这让你更容易去理解它。这同样意味着避免了在初始化数据模型时的时间损失。
# 自定义属性
再举一个例子, Element
节点在 Slate
中是这样子的:
interface Element {
children: Node[]
[key: string]: unknown
}
2
3
4
这是非常宽松的接口。它要求有一个 children
属性来定义所包含的子节点。
但是你可以使用自定义的属性来扩展元素 (或者其他接口)。比如,你可能有一个 paragraph
和 link
元素:
const paragraph = {
type: 'paragraph',
children: [...],
}
const link = {
type: 'link',
url: 'https://example.com',
children: [...]
}
2
3
4
5
6
7
8
9
10
type
和 url
属性就是你的自定义 API 。Slate
知道这些属性的存在,但是它不知道这些属性能干嘛。不过,当它渲染一个 link
元素的时候,你会收到一个包含这些自定义属性的对象。所以你可以这样去渲染它:
<a href={element.url}>{element.children}</a>
在使用 Slate
的初始阶段,理解它定义的所有接口是很重要的。在每篇指南中都会有对于接口的一些讨论。
# 辅助函数
除了输入信息之外,Slate
的每个接口也会暴露一系列的辅助函数,使它们更容易使用。
比如,在处理节点时:
import { Node } from 'slate'
// 获取节点的文本内容。
const string = Node.string(element)
// 在根节点的特定路径处获取节点。
const descendant = Node.get(value, path)
2
3
4
5
6
7
或者,在使用 ranges (文档片段)
的时候:
import { Range } from 'slate'
// 按顺序获取一个 range 的开头和结尾
const [start, end] = Range.edges(range)
// 检查一个 range 是否是一个单点 (起止位置和终止位置是同一点)
if (Range.isCollapsed(range)) {
// ...
}
2
3
4
5
6
7
8
9
在处理不同的接口时,有大量的辅助函数可以用于所有常见的用例。当你开始写代码的时候,最好把他们都阅读一遍,因为通常你可以把复杂的逻辑简化为几行辅助函数代码。
# 自定义辅助函数
除了内置的辅助函数,你可能想要定义自己的辅助函数并且暴露在自定义的命名空间里面。
举个例子,如果你的编辑器支持图片,你可能需要一个辅助函数来判断一个元素是不是图片元素:
const isImageElement = element => {
return element.type === 'image' && typeof element.url === 'string'
}
2
3
你可以轻松地把它们定义为一次性函数。但是你也可以像核心接口那样把它们绑定到命名空间,再去使用它们。比如:
import { Element } from 'slate'
// 你可以在任何地方使用 `MyElement` 来访问你的扩展。
export const MyElement = {
...Element,
isImageElement,
isParagraphElement,
isQuoteElement,
}
2
3
4
5
6
7
8
9
这样可以轻松地将定制域的逻辑与内置的 Slate
辅助函数一起重用。