梦亦同趋
首页
记忆
发现
yjs

y-websocket源码学习

官方的同步示例我觉得还是很有参考价值的,值得深入学习下,所以这里扒了下源码。

Aling
2023-09-15
阅读量:151
阅读时长

Yjs.js

基础概念
Document Updates
Delta Format
网络同步
Shared Types
Y.Doc
Y.Event
Y.UndoManager
y-protocol源码学习
y-websocket源码学习

官方的同步示例我觉得还是很有参考价值的,值得深入学习下,所以这里扒了下源码。需要注意的是这里面涉及到了yjs和y-protocols。请务必先了解这两个库。

点击查看源码

上述源码代码过多同时涉及到yjs其他模块,我们梳理以下y-websocket的流程架构:

#初始化对象

类似大多数ws通信初始化方式,我们通过new WebsocketProvider来创建ws连接,yjs称其实例化对象为provider,我们后面也称其为provider。 初始化参数我上面都有注释,大部分很好理解,我们这只单独说一下WebSocketPolyfill这个参数。官网对这个参数的解释是当在node环境下使用这个库时需要配置该参数。原因是浏览器环境下环境变量中有Websocket这个对象了可以直接调用,node全局环境下没有这个对象所以需要额外提供,这个参数本质上就是一个Websocket对象(类)。以下是官网的代码实例:

const wsProvider = new WebsocketProvider('ws://localhost:1234', 'my-roomname', doc, { WebSocketPolyfill: require('ws') })

当然你硬要在浏览器环境下传也可以,就像下面这样: const wsProvider = new WebsocketProvider('ws://localhost:1234', 'my-roomname', doc, { WebSocketPolyfill: Websocket })

初始化过程中会初始化一些属性,这些没什么好说的。其中有个this.mux = mutex.createMutex()属性,是在对象实例上创建了一个互斥锁。这个方法确保了同一时刻只有一个mux函数在执行,如下是官网的示例:

js
const mutex = createMutex() mutex(() => { // This function is immediately executed 该函数将会立即执行 mutex(() => { // This function is not executed, as the mutex is already active. 这个函数将不会被执行,因为有正在执行的mutx函数 }) })

上图示例中在mutex函数内部调用的mutex函数是不会执行的,因为执行内部函数时外层的函数没还没执行结束,相当于被锁住了。

#初始化连接(connect)

初始化连接有两种方式,执行new WebsocketProvider时传递connect参数则会自动连接,否则需要手动调用connect()连接。

connect方法内部会首先调用setupWS方法构建websocket连接。

#setupWS

  • 给websocket实力绑定活动事件
  • 设置provider状态为连接中

随后调用connectBc方法初始化本地跨标签页更新

#connectBc

  • 订阅当前房间号的事件,当其他标签页修改时会触发该事件,通过获取该事件的参数即可实现本地跨标签同步。
  • 通过y-protocol协议同步文档信息,同步意识信息

#关闭连接(disconnect)

  • 设置shouldConnect为false
  • 调用disconnectBC方法
  • 关闭websocket连接

#disconnectBc

  • 通知其他人本机已离线
  • 关闭标签页监听

#活动事件

项目下的事件活动主要在这里处理。

#消息接受(onmessage)

  • 记录消息的接收时间
  • 将数据实例化,开始读取信息
    • 创建回复的消息(encoder)
    • 判断信息的类型,意识信息或文档同步信息在这里区分
    • 是文档同步信息。进入下层(y-protocol)协议
      • 判断信息类型(step1/step2)
      • step1消息。消息主体为远程文档的状态向量,将该状态向量与本地文档状态比较生成更新数据并封装成step2类型的消息(encoder)
      • step2消息。消息主体为当前文档和发送方文档的差异数据,将这些差异同步到本地文档以实现文档状态同步。
    • 是意识信息
      • ...
    • 是身份信息
      • ...
    • 发送回复消息(encoder)
  • 回复消息数据为空则不发送数据,否则广播发送数据

#连接成功(onopen)

  • 更新provider的状态为连接成功
  • 创建step1消息,广播该消息到其他远程客户端
    • 连接成功后发送step1消息必要性?事实上在初始化一个WebsocketProvider的时候,我们会传入一个doc,这个doc是从服务器获取的最新的文档状态。我觉得连接成功后发送一次step1主要还是为了能获取到最新的文档状态,因为这个文档可能正有其他用户在编辑中(最新的版本),编辑中的文档会产生一些尚未持久化的差异,这些差异并不会立即同步到数据库中,而初始化中获取的初始文档数据大概率是从数据库中读取的文件,我们需要一次同步操作来使新加入的用户能立即获取到未持久化的最新编辑中。
    • 广播意识信息,告诉其他用户我已上线

#连接关闭(onclose)

  • 清除websocket实例
  • 清除自身以外的意识信息

#Usages

#status Event

WebsocketProvider对象继承自lib0库的Observable类。这个类就是个订阅发布模式的实现,WebsocketProvider类内部触发status事件来暴露连接的状态。我们可以绑定响应的监听事件函数来处理连接状态的变化。

#synced Getter

当该字段变化时,WebsocketProvider内部会触发synced和sync事件.同样可以绑定对应的监听函数来满足一些业务上的需求(比如,保存中?或者加载中?)

上次更新:2025-09-01 09:46:43
y-protocol源码学习