# 迁移

从更早的 Slate 版本迁移到 0.50.x 版本不是一个简单的任务。整个框架被重构 ( re-considered from the ground up) 。这导致了 更好的 抽象,让你可以编写更少的代码。但是迁移过程一点都不简单。

强烈建议你在阅读完这篇指南后,阅读全新的 Walkthroughs 和另一个 Concepts 来了解如何应用所有新的概念。

# 主要的区别(Major Differences)

从架构的角度看,这里是 0.50.x 版本的 Slate (和之前版本) 主要 区别的概览。

# JSON!

数据模型现在由简单的 JSON 对象组成。之前,它使用 Immutable.js (opens new window) 数据结构。这是一个巨大的改变,并且它带来了一些其他的东西。它(也很可能)会提升使用 Slate 的平均性能。它也使新手更容易入门。从中迁移是一个巨大的改变,但是这是值得的!

# 接口(Interfaces)

数据模型(现在)是基于接口的。之前每一个模型都是一个类的实例。现在, (数据模型)不仅是一个数据纯对象,而且 Slate 也期待这个对象实现一个接口。所以过去位于 node.data 的自定义属性现在可以位于节点的顶层。

# 命名空间(Namespaces)

在命名空间中,辅助函数集合暴露出大量的辅助函数。比如, Node.get(root, path) 或者 Range.isCollapsed(range) 。结果就是使代码更加清晰,因为你总是可以快速查看你正在使用的接口。

# TypeScript

现在代码库使用 TypeScript。使用纯 JSON 作为数据模型,并且使用基于接口的 API, 通过迁移到 TypeScript 可以简化这两件事。你不需要自己使用它,但是如果你使用它,则在使用 API 的时候会获得更多的安全性。 (而且,如果你使用 VS Code,无论如何你都会获得更好的自动补全!)

# 更少的概念(Fewer Concepts)

接口和命令的数量已经减少了。之前的 SelectionAnnotationDecoration 曾经是单独的类。现在它们是实现 Range 接口的简单对象。之前的 BlockInline 是单独定义的,现在它们是一个实现 Element 接口的对象。 之前有 DocumentValue,但是现在顶级的 Editor 节点包含了文档本身的子节点。

命令的数量也减少了。之前我们每一个输入类型都拥有一个命令,像是 insertTextinsertTextAtRangeinsertTextAtPath。它们已经被合并到一个更可定制的命令的更小的集合,比如 insertText 可以在 Path | Range | Point 中使用。

# 更少的包(Fewer Packages)

为了减轻维护负担,和因为 Slate 核心新的抽象和 API, (它们) 使事情变得更加容易, (所以) 目前包的数量已经减少了。 像是 slate-plain-serializerslate-base64-serializer等已经被移除了,并且如果需要的话,可以在用户域中轻松实现。甚至 slate-html-deserializer 可以在用户域中实现 (in ~10 LOC leveraging slate-hyperscript)。并且不再暴露像是 slate-dev-environmentslate-dev-test-utils之类的内部包,因为它们是实现细节。

# 命令(Commands)

添加了新的命令概念。 (旧的命令概念现在被称为转换:transforms。)新概念表达了用户编辑文档的语义化意图。并且它们允许对用户行为进行正确的抽象 — 比如改变当用户按下回车键或删除键时发生的情况等等。你应该重写命令行为来代替使用 keydown 事件。

命令通过调用 editor.* 的核心函数来触发。然后会依次通过类似中间层的栈,但是它们是由合成函数构建的。任何插件都可以通过重写编辑器行为来扩展它。

# 插件(Plugins)

插件现在是一个纯函数,可以增强接收到的 Editor 对象并且返回它们。比如可以通过编写 editor.exec 函数来增强命令执行。或者通过编写 editor.apply 来监听操作。之前它们依靠自定义的中间件栈,它们只是合成到编辑器的处理程序。现在我们使用纯的老的函数合成方式 (也称为包装:wrapping) 来替代它。

# 元素(Elements)

块元素和行内元素现在是运行时进行的选择。之前它是被通过 object: 'block' 或者 object: 'inline' 属性被绑定到数据模型中的。现在,它在运行时检查一个元素是否是行内元素。比如,你可能需要检查 element.type === 'link' ,然后把它看作是一个行内元素。

# 更加React化(More React-ish)

插件已经不再关心渲染和事件处理。之前插件可以完全控制渲染逻辑和编辑器事件处理逻辑,这产生了一个坏的激励:把所有的渲染逻辑放到插件里面,这导致了 Slate 完全被 React 包装 (这很难做好) 。取而代之的是,现在新的行为是插件只专注在富文本方面,而把渲染和事件处理交给 React。

# 上下文(Context)

之前 <Editor> 组件不光是一个控制器对象,还是可编辑的 DOM 元素。这给其它 Slate 组件和它一起使用带来了大量的问题。在新版本中,有一个新的 <Slate> 上下文 (provider) 组件,和一个更简单的 <Editable>contenteditable 组件。通过提升 <Slate> 上下文到你的组件树中更高的位置,你可以使用 useSlate hook 把编辑器分享给工具栏,按钮等等。

# Hooks

除了 useSlate hook,还有一些其它的 hooks。比如 useSelecteduseFocused hooks 有助于了解何时渲染被选择的状态 (通常用于空节点) 。并且因为使用 React 的 Context API,当状态发生变化的时候它们会自动重新渲染。

# beforeinput

我们现在一般只使用 beforeinput 事件。代替依赖一系列的 shimsReact 合成事件的奇怪行为,我们现在使用标准的 beforeinput 事件作为我们的基础。它完全支持 SafariChrome, 不久后就会支持新的基于 Chromium 内核的 Edge,并且目前已经可以在 Firefox 上使用了。与此同时,我们有一些针对 Firefox 的补丁让它运行。开箱即用的 Slate 核心不再支持 Internet Explorer

# History-less

现在操作历史的核心逻辑最终被提取到一个独立插件中。这使人们更容易实现自定义的操作历史行为。并且确保了插件有足够的控制权以复杂的方式增强编辑器,因为历史记录需要它。

# 无标记(Mark-less)

标记已经被从 Slate 数据模型移除。现在我们有能力正确地在节点上定义自定义属性,你可以为文本节点的自定义属性建立标记模型。比如加粗可以被简单的建模为 bold: true 属性。

# 无注释(Annotation-less)

熟悉地,注释 (annotations)已经从 Slate 核心移除。现在,通过定义自定义操作和使用装饰 (decorations)来渲染注释范围,从而完整地实现在用户环境 (userland)中。但是大多数情况下应该使用自定义文本节点属性或者装饰。没有多少使用注释受益的用例。

# 减少体积(Reductions)

一个目标是大大简化 Slate 的大量逻辑使它更容易理解和迭代。这可以通过重构为更好的抽象表达来做到 (凭借现代 DOM API,和迁移到更简单的 React 模式)。

使你对总代码行数的更改有所了解:

slate                       8,436  ->  3,958  (47%)
slate-react                 3,905  ->  1,954  (50%)

slate-base64-serializer        38  ->      0
slate-dev-benchmark           340  ->      0
slate-dev-environment         102  ->      0
slate-dev-test-utils           44  ->      0
slate-history                   0  ->    211
slate-hotkeys                  62  ->      0
slate-html-serializer         253  ->      0
slate-hyperscript             447  ->    345
slate-plain-serializer         56  ->      0
slate-prop-types               62  ->      0
slate-react-placeholder        62  ->      0

total                      13,807  ->  6,468  (47%)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

这是相当大的变化!这甚至不包括(框架逻辑)流程中摆脱的依赖关系。