Slate.js 之 Operation 概述
Slate.js 介绍
Slate 是一个 L1 级的富文本编辑器框架,其架构分为四个部分:核心逻辑层slate
、视图层slate-react
、历史操作层slate-history
以及用于复制粘贴外部内容的 helper slate-hyperscript
。
本文的主题是 slate
层中的一个概念,Operation。
Operation
Operation 是 Slate 中进行变换的粒度最小的原子操作,所有的高级变换(Transforms API)归根结底其实都只是一个或者多个 Operation 的封装打包。
Operation 能够帮助我们实现历史操作(撤销/重做)、协同编辑等功能。如此重要的内容,却缺乏官方的 API 文档作为参考,由是撰写本文。
规范
Opertaion 原子操作,它是一个对象,该对象必须包含属性 type
。
type
的取值为以下 Slate 中支持的总共 9 种 Operations。
其中 insert_node
和 remove_node
、insert_text
和 remove_text
、merge_node
和 split_node
三对操作互为逆运算,而余下的三者的逆运算为自身。
逆运算的意思是,两个 Operation 执行的操作得到的效果是完全相反的。
互为逆操作
举个例子,我想“插入文本”——在 { path:[0], offset: 0}
这个 Point 插入一个字符串 "hello"。
e.apply( {type: 'insert_text', path: [0], offset: 0, "hello"} ) 复制代码
按照对于上图的理解,将 insert_text
改成逆操作 remove_text
,以下代码可撤销掉“插入文本”。
e.apply( {type: 'remove_text', path: [0], offset: 0, "hello"} ) 复制代码
Slate 本身提供了 Operation.inverse(op)
的方法。输入、输出均是 Operation,可用它替我们撤销“插入文本”。
e.apply( Operation.inverse( {type: 'insert_text', path: [0], offset: 0, "hello"} ) ) 复制代码
源码中,
Operation.inverse(op)
对于type
为insert_text
的情形,所做的事情也不过是将type
用remove_text
替换,然后返回这个 Operation 出来。
逆运算为自身
伪原创工具 SEO网站优化 https://www.237it.com/
对于逆运算为自身的情况,move_node
、set_node
和 set_selection
。
// 移动节点 e.apply( {type: 'move_node', path: [1], newPath: [0]} ) // 逆运算如下 e.apply( {type: 'move_node', path: [0], newPath: [1]} ) // 设置节点属性 e.apply( {type: 'set_node', path: [0], properties: {lineHeight: 1.75}, newProperties: {lineHeight: 2}} ) // 逆运算如下 e.apply( {type: 'set_node', path: [0], properties: {lineHeight: 2}, newProperties: {lineHeight: 1.75}} ) // 设置选区属性 e.apply( {type: 'set_selection', properties: null, newProperties: {bold: true}} ) // 逆运算如下 e.apply( {type: 'set_selection', properties: {bold: true}, newProperties: null} ) 复制代码
move_node
、set_node
和 set_selection
三者有一个共同的特点,就是 Operation 中含有一个 X
,同时又存在一个 newX
。当他们取逆运算的时候,只需要将 X
与 newX
对调即可。
同样,你也可以并且应该使用 Operation.inverse(op)
更高效地实现逆运算。
e.apply( Operation.inverse( {type: 'set_selection', properties: null, newProperties: {bold: true}} ) ) 复制代码
上述仅描述 Operation 作为一个接口时体现的特性,具体涉及到在 editor
中应用某个操作的逻辑,可以参考transform。
应用
历史操作
由于 Operation 支持互逆操作的特性,因此可以将它们以数组的形式存储起来,用来保存历史操作。
如 slate-history
中提供的 HistoryEditor
包装了两个数组(undo
和 redo
) 到 editor
实例上。
e.history = {undo: [], redo: []} 复制代码
协同编辑
Slate 的协同是基于交换可复制数据类型(CRDT, Commutative Replicated Data Types),目前的技术包括slate-collaborative 以及 Yjs。
Operation 提供了最小粒度的原子操作,而至于操作之间如何优雅地(即保留作者意图的信息地)合并在一起,由这篇译文讲解算法相关的逻辑。
作者:swanf
链接:https://juejin.cn/post/7034480408888770567