# 渲染
Slate
最棒的地方之一就是使用 React
来渲染界面,所以它很适合你的应用程序。Slate
不会重新发明视图层让你去学习。它尝试尽可能保持 React
化。
最后,Slate
让你可以控制你的富文本域中自定义节点的渲染行为。
你可以通过传递渲染属性 (props
) 到顶级的 <Editable>
组件来定义这些行为。
举例来说,如果你想要渲染自定义元素组件,你可以传递 renderElement
属性 (prop
):
import { createEditor } from 'slate'
import { Slate, Editable, withReact } from 'slate-react'
const MyEditor = () => {
const editor = useMemo(() => withReact(createEditor()), [])
const renderElement = useCallback(({ attributes, children, element }) => {
switch (element.type) {
case 'quote':
return <blockquote {...attributes}>{children}</blockquote>
case 'link':
return (
<a {...attributes} href={element.url}>
{children}
</a>
)
default:
return <p {...attributes}>{children}</p>
}
}, [])
return (
<Slate editor={editor}>
<Editable renderElement={renderElement} />
</Slate>
)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
🤖 请确保在你自定义的组件里面混入了
props.attributes
并且渲染了props.children
!这些属性必须被添加到组件内部的顶级DOM
元素中,因为它们是Slate
的DOM
辅助函数渲染 (处理) 所必须的。children
是Slate
自动为你管理的文档的实际文本内容。
你不只可以使用简单的 HTML
元素,还可以使用自定义的 React
组件
const renderElement = useCallback(props => {
switch (props.element.type) {
case 'quote':
return <QuoteElement {...props} />
case 'link':
return <LinkElement {...props} />
default:
return <DefaultElement {...props} />
}
}, [])
2
3
4
5
6
7
8
9
10
# 叶子节点(Leaves)
当渲染包含格式的文本时候,这些文本会被分组为一个个“叶子文本”,然后再应用它们相应的格式。
为了自定义每个叶子的渲染,你可以使用自定义的 renderLeaf
属性:
const renderLeaf = useCallback(({ attributes, children, leaf }) => {
return (
<span
{...attributes}
style={{
fontWeight: leaf.bold ? 'bold' : 'normal',
fontStyle: leaf.italic ? 'italic' : 'normal',
}}
>
{children}
</span>
)
}, [])
2
3
4
5
6
7
8
9
10
11
12
13
请注意,我们处理它的方式和 renderElement
有一点不同。因为文本格式的文本很简单,所以我们放弃使用 switch
语句,而是仅仅对少量的样式进行开关切换。(但是如果你更喜欢使用自定义组件,没有谁可以阻止你使用!)
对于文本格式级别的一个缺点是:你不能保证给定的格式都是“可连续的”的 — 这意味着只能作为一个单叶子。这个限制看起来和 DOM 很相似,这是一种无效的情况:
<em>t<strong>e</em>x</strong>t
因为上面这个例子的元素没有正确的关闭自己,所以它们是无效的。相应的,你应该把上述 HTML
写成这个样子:
<em>t</em><strong><em>e</em>x</strong>t
如果你碰巧在文本中添加了另一个交叉 (<strike>
) 的标签,则有可能会重新排序结束标记。在 Slate
中渲染叶子也是这样 — 即使每一个单词都应用了格式,你也不能保证叶子就会是连续的,因为它取决于和其他格式交叉的方式。
当然,叶子的这些内容听起来十分复杂。但是,只要按照以下目的去使用文本级别格式和元素级别格式,你就不必过多地去思考它们:
- 文本属性用于 不连续的,字符级别的格式。
- 元素属性用于文档中 连续的,语义级别的元素。
# 装饰(Decorations)
装饰是另一个类型的文本级别格式。它们类似于常规的自定义属性,不同之处在于每一个属性都适用于文档片段 (Range
),而不是与给定的文本节点关联起来。
然而,装饰是一个完全的基于它的内容在渲染时期计算出来的。这是有用的对于动态格式,比如像是语法高亮和搜索文本,当内容 (或外部数据) 发生变化的时候,有可能会改变格式。
# 工具栏(Toolbar), 菜单(Menu), 叠加层(Overlays), 或者更多!
除了在 Slate
内部控制渲染节点,你也可以通过使用 useSlate
hook
,从其他的组件获取到当前的编辑器上下文。
这样其他组件就可以执行命令,查询编辑器状态,以及更多。。。
一个常见的用例就是渲染一个带有格式化按钮的工具栏,这些按钮会基于当前的选择范围 (selection
)来突出显示(是否可用):
const MyEditor = () => {
const editor = useMemo(() => withReact(createEditor()), [])
return (
<Slate editor={editor}>
<Toolbar />
<Editable />
</Slate>
)
}
const Toolbar = () => {
const editor = useSlate()
return (
<div>
<Button active={isBoldActive(editor)}>B</Button>
<Button active={isItalicActive(editor)}>I</Button>
</div>
)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
因为 <Toolbar>
使用了 useSlate
hook
去获取内容上下文,所以当编辑器变化的时候他会重新渲染,因此按钮的激活状态是同步的。