Skip to content

Latest commit

 

History

History
71 lines (45 loc) · 7.37 KB

yjs-fqa.md

File metadata and controls

71 lines (45 loc) · 7.37 KB

主要收集 slate 集成 yjs 的常见问题,技术方案是 slate + slate-angular + y-slate(slate-yjs) + yjs。

y-slate 是基于 slate-yjs 的修改版本,没有使用 slate-yjs 是因为我们视图层是基于 angular 的版本,slate-yjs 有一些不兼容的地方。

1. toJSON

弃用 slate-yjs 中的方法 toSlateDoc ,改用 Yjs 本身的 toJSON 方法(性能问题)

2. 表格合并单元格撤回同步失败

slate 数据转 yjs 数据的过程中,同步 set_node 操作到 yjs 数据结构是移除的属性没有在 yjs 数据结构中删除,导致同步失败(slate-yjs 缺陷)。

只有撤回操作才有的问题,当前基于 Yjs UndoManage 实现撤回时应该不会遇到这问题了,但是这是数据 slate-yjs 实现 slate 操作转 yjs 数据结构时本身的缺陷

3. 页面初始化后编辑器直接崩溃(slate 数据和 yjs 数据对应不上)

初始化过程采用 applyYjsEvents ​的方式,一些不太标准的 slate 数据会触发 normalize,而这是个时候的数据变更是不会同步给 yjs 的(asRemote控制),所以 slate 和 yjs 的数据无法对应上,那么整个同步机制就会受影响。

4. Undos/Redos 管理,需要借助Yjs的 UndoManage 的实现

Slate 提供的 slate-history 是不具备协同编辑处理能力的,即使可以通过 withoutMerging 实现只入栈本地的操作记录,但是这样操作栈的选区会因为忽略协同者的操作而出现脏路径,导致撤回异常。 所以 Undos/Redos 的管理是必须要通过一种无冲出的方式实现的。

这里要实现的 Undos 管理是用户的撤回栈只能撤回自己的操作,协同者的修改是不会记录到撤回栈。

5. 撤回操作导致编辑器崩溃(触发了 normalize )

应用了 yjs 的 undos 管理后,撤回操作的数据流是 yjs -> slate 这样,数据修改的触发源是 yjs 数据结构的修改,从 yjs -> slate 数据同步是产生的 slate 操作是不会再向 yjs 同步的,这时如果触发了 normalize 会造成数据的修改无法同步给 yjs(当做远程操作了),导致数据不一致,编辑器崩溃。目前的处理办法是在处理 yjs -> slate 数据同步时禁用 noramalize,这样处理也合理,normalize 只在本地操作中触发。

6. 协同者焦点异常

image.png 这其中涉及到焦点位置的转换(absolute -> relative),这有点细节,yjs 在进行焦点定位时是相对定位,上图中 Alexane ​ 的焦点位置与 *秋天不会远了* *​ * 这句话对应的数据结构关联关联,​理论上即使这句话应为回车操作变到了第二行,焦点位置应该也可以定位准确,但是因为数据操作的原因,从 slate -> yjs 数据变化的时候,导致 *秋天不会远了* 这句话先在第一行被删除,然后再在第二行被插入,那么 Alexane ​ 的位置被关联在了一个已经被删除的数据结构上,导致无法正确定位。 回车后的焦点位置如下:(理论上 Alexane ​ 的位置是不符合预期的,但是也可以接受) image.png

额外问题: 1. 两个人在同一行在段落,一个用户对一段文字进行了加粗操作后,另外一个用户焦点会意外跳动

7. 开启实时协同编辑总是Loading中

这个是整个 y-protocol 机制的问题,服务端因为要进行 Auth 验证可能会有一定延时后才开始监控 onmessage 事件,如果前端在发送 step1 时服务端还没有开始监控 onmessage ,那么客户端可能会接收不到 step2 的消息,进而无法初始化完成,所以一直在Loading。

8. 路径变换问题

这个是一个容易理解的问题,就是编辑器在处理弹框时,可能会存储一个操作的路径(Path),如果是单机编辑那没有任何问题,但是如果是实时协同编辑,那随着协同者的操作(插入/删除节点),这个路径可能会变化,这个时候再使用这个 Path 就会导致操作异常,所以需要使用 Slate 提供的 PathRef 记录每次变换后的Path,应用操作时使用最新的路径就可以了。

9. 弹框位置冲突

多人实时协同编辑测试的过程中发现太多,弹框冲突的问题,以前的弹框实现方案,很多是基于onChange事件做的,但是协同编辑其实要求,其它协同者数据更改触发的onChange对我的弹框状态不要产生影响,涉及到的有 提及插件、标签插件、链接插件等,还有弹框位置的更新(尤其是placeholder、块级菜单)

10. 点击聚焦 - 光标闪烁后跳动到原来的位置

现象:当协同编辑人数较多、并且操作比较频发是,通过鼠标点击移动光标位置,会有一定的概率导致焦点移动失败,跳回原来的地方。 目前的办法是移除 selectionchange 的防抖函数 throttle (并未完全解决问题)

问题原因分析:

通过点击触发了 slate-react 底层的 selectionchange 事件,但是它有一个防抖的延时(100ms),所以这个时候不会立即执行浏览器原生选区 到 Slate 选取的同步,假如在这个100ms 时间内有一个远程操作传递过来,slate 在应用这个远程操作时,会重新计算 slate 选区的状态,然后根据新计算的选区状态直接更新浏览器原生选区,导致刚刚通过点击操作产生的选区更新被重置。

11. Yjs UndoManage 的 captureTimeout 参数

操作合并逻辑? Slate 的撤回栈操作合并逻辑来源于操作类型的判断 Yjs UndoManage 撤回栈合并的逻辑是基于时间差的 如果操作产生的时间间隔小于一个值则进行撤回栈的合并。

12. Yjs 中的操作意图流失

move 对应先删除再插入 split_node 也是对应也是删除和插入

13. 两个光标在同一个段落导致中文输入被打断问题

这个问题根源在编辑器的实现机制,绘制协同者的光标时通过 decorate 模式实现的,当协同者位置发生变换时,当前段落对应的DOM其实会触发删除和重新创建的,这个时候加入另外一协同者正在当前段落输入中文,由于焦点所在的DOM被移除,这个时候中文组合输入会被打断,焦点自动跳到段落的开始位置,这是问题其一,其二是编辑器视图层底层监控的 compositionend 事件不会再次被触发,那么底层针对 isComposing 状态做的特殊出都会变的失效,导致无论协同者无论怎么移动光标位置都不会触发 slate 选区的同步,编辑器无法继续使用,除非再次进行中文输入 重新触发 compositionstart/compositionend 事件把 isComposing 调整正确才可以,这个问题相当严重。 修复思路: 在视图层进行视图层刷新是做这种 ​脏状态 ​的判断,如果 isComposing = true 并且 DOM 中不存在 compositionText 则可以认为当前状态是脏状态,说明中文组合输入被意外的打断,那么直接把 isComposing 状态修正为 false,然后重新定位焦点到原本位置。

14. 加粗斜体等标记null问题

跟问题「2. 表格合并单元格撤回同步失败」类似,给元素取消属性标记时,没有正确同步到 yjs 数据结构中, slate-yjs 实现问题。