文档阅读——y-websocket
本人项目需要使用Yjs进行协同文档的设计,早期使用了其他开发好的组件,但是随着客户需求的变更,现在需要自主设计同步类型,故开始阅读研究websocket的使用,所幸Yjs提供了websocket类型的Provider,能够节省不少开发的时间。
文章目录
y-websocket
Websocket Provider实现了一个传统的客户端-服务器模型。这意味着它遵循标准的网络通信模式,其中客户端通过Websocket连接到一个单一的服务器端点,服务器负责在客户端之间分发文档更新和感知信息(如用户的在线状态、光标位置等)。
更进一步的,用户可以通过在服务器上配置Provider,这将能够持久化Doc对象更新或扩展基础设施。例如,你可以将文档更新保存到数据库中,或者通过添加更多服务器来处理更多的客户端连接。
如果使用Websocket Provider作为中心源来处理认证和授权,则服务器可以管理用户身份验证和权限控制,并且Websocket通信可以发送头部信息和cookies,因此可以使用现有的认证机制与服务器交互。这允许集成现有的用户认证系统。
y-websocket主要包含两个特点:
- 支持跨标签页通信。当你在同一浏览器的不同标签页中打开相同的文档时,文档的更改可以通过跨标签页通信来交换。当你在同一浏览器的不同标签页中打开相同的文档时,文档的更改会通过广播频道(Broadcast Channel)进行交换,如果广播频道不可用,则回退到
localStorage
。 - 支持交换意识信息,例如光标位置。这意味着当多个用户编辑同一文档时,你可以看到其他用户的光标位置和其他相关信息。
快速开始
npm i y-websocket
import * as Y from 'yjs'
import { WebsocketProvider } from 'y-websocket'
const doc = new Y.Doc()
const wsProvider = new WebsocketProvider('ws://localhost:1234', 'my-roomname', doc)
wsProvider.on('status', event => {
console.log(event.status) // logs "connected" or "disconnected"
})
这段代码设置了一个Yjs文档实例,并通过WebsocketProvider连接到一个本地服务器,用于在指定的房间名称下同步文档状态。同时,它还设置了一个事件监听器来监听连接状态的变化,并在控制台输出连接或断开连接的信息。
特别的,在NodeJS中
WebSocket 提供程序需要 WebSocket 对象来创建与服务器的连接。在NodeJS环境下,可以使用 ws
包在 NodeJS 中填充 WebSocket 支持。
npm i ws
const ws = require('ws')
const wsProvider = new WebsocketProvider(
'ws://localhost:1234', 'my-roomname',
doc,
{ WebSocketPolyfill: ws }
)
WebSocketPolyfill
在 y-websocket
或其他类似库中的主要作用是确保WebSocket连接的兼容性和可靠性,即使在NodeJS环境中可能没有原生的WebSocket支持。
除了基本的WebSocket功能,WebSocketPolyfill
可能会提供额外的功能,比如自动重连、心跳机制(ping/pong)
来保持连接活跃、更好的错误处理等。并且WebSocketPolyfill
允许用户修改或扩展WebSocket的行为,以适应特定的需求。
Websocket的启动
在 y-websocket 的 Github 仓库中,给出了直接通过 y-websocket 启动一个Websocket服务器的基本命令。
修改于 2025/04/06
我的电脑由于不可抗力进行了系统重装,重装后发现无法直接使用y-websocket快速启动服务器
根据github的内容显示,现需要安装@y/websocket-server
后才可启动npm install @y/websocket-server
Linux
HOST=localhost PORT=1234 npx y-websocket
Windows
$env:HOST="localhost";$env:PORT="1234"; npx y-websocket
以上是最简单的websocket服务器启动命令,将会在本地1234端口启动默认的Websocket服务器。
如果需要自定义相关内容,则需要使用具体的配置文件启动传统Websocket服务器。
创建一个名为server.js
的文件,并添加以下代码
const WebSocket = require('ws');
// 创建WebSocket服务器实例,监听在8080端口
const wss = new WebSocket.Server({ port: 8080 });
// 监听连接事件
wss.on('connection', function connection(ws) {
console.log('Client connected');
// 监听客户端发送的消息
ws.on('message', function incoming(message) {
console.log('received: %s', message);
// 将接收到的消息发送回客户端
ws.send(`Hello, you sent -> ${message}`);
});
// 监听连接关闭事件
ws.on('close', () => {
console.log('Client disconnected');
});
});
console.log('WebSocket server is running on ws://localhost:8080');
在命令行中运行服务器
node server.js
持续化Websocket服务器
PORT=1234 YPERSISTENCE=./dbDir node ./node_modules/y-websocket/bin/server.js
该命令将文档更新保留在 LevelDB 数据库中。有关更多信息,请参阅 LevelDB 持久性。
这部分内容我后续会研究使用Redis进行持续化,故这里先不展开
带有HTTP返回的Websocket服务器
为了在文档更新时向 HTTP 服务器 (POST) 发送去抖动回调,可以采用以下环境变量:
CALLBACK_URL
:回调服务器 URL
CALLBACK_DEBOUNCE_WAIT
:回调之间的去抖动时间(以毫秒为单位)。默认为 2000 毫秒
CALLBACK_DEBOUNCE_MAXWAIT
:回调前等待的最长时间。默认为 10 秒
CALLBACK_TIMEOUT
:HTTP 调用的超时时间。默认为 5 秒
CALLBACK_OBJECTS
:用于获取数据的共享对象的 JSON('{"SHARED_OBJECT_NAME":"SHARED_OBJECT_TYPE}')
CALLBACK_URL=http://localhost:3000/ CALLBACK_OBJECTS='{"prosemirror":"XmlFragment"}' npm start
这会在收到更新(默认 DEBOUNCE_WAIT
)后 2 秒向 localhost:3000
发送一个去抖动回调,更新内容为主体中名为“prosemirror”
的 XmlFragment
的数据。
另外
就我查到的资料来说,通常是说WebsocketProvider
不会自动配置 WebSocket 服务器。WebsocketProvider
仅仅是一个客户端库,它用于连接到一个已经运行并配置好的 WebSocket 服务器。
但是在我的实践中,我发现我项目迁移到新电脑后,直接在vue页面中使用y-websocket连接到原端口上不存在的 websocket 服务器时,该项目依旧能够实现协同通信的效果,我也找不到我有写过什么自动启动的脚本,令人感慨,不知道是不是使用Nuxt进行部署会有什么特殊之处。
API
import { WebsocketProvider } from 'y-websocket'
-
wsProvider = new WebsocketProvider(serverUrl: string, room: string, ydoc: Y.Doc [, wsOpts: WsOpts])
创建一个新的 websocket-provider 实例。只要此提供程序或连接的ydoc
未被销毁,更改将通过连接的服务器同步到其他客户端。或者,您可以指定一个配置对象,以可以覆盖以下wsOpts
提供的默认值。wsOpts = { // 如果您想使用 wsProvider.connect() 手动连接,请将其设置为 `false` connect: true, // 指定将进行 url 编码并附加到 `serverUrl` 的查询字符串 // 即 params = { auth: "bearer" } 将转换为 "?auth=bearer" params: {}, // Object<string,string> // 您可以 polyill Websocket 对象 (https://developer.mozilla.org/en-US/docs/Web/API/WebSocket)。 // 例如在 nodejs 中,您可以指定 WebsocketPolyfill = require('ws') WebsocketPolyfill: Websocket, // 指定现有的 Awareness 实例 - 请参阅 https://github.com/yjs/y-protocols awareness: new awarenessProtocol.Awareness(ydoc) }
-
wsProvider.wsconnected: boolean
如果此实例当前已连接到服务器,则为 True。 -
wsProvider.wsconnecting: boolean
如果此实例当前正在连接到服务器,则为 True。 -
wsProvider.shouldConnect: boolean
如果为False,客户端将不会尝试重新连接。 -
wsProvider.bcconnected: boolean
如果此实例当前正在通过 BroadcastChannel 与其他浏览器窗口进行通信,则为 True。 -
wsProvider.synced: boolean
如果此实例当前已连接且与服务器同步,则为 True。 -
wsProvider.disconnect()
断开与服务器的连接并且不要尝试重新连接。 -
wsProvider.connect()
与 websocket 服务器建立 websocket 连接。如果您最近断开连接或设置了wsOpts.connect = false
,请调用此方法。 -
wsProvider.destroy()
销毁此wsProvider
实例。断开与服务器的连接并删除所有事件处理程序。 -
wsProvider.on('sync', function(isSynced: boolean))
添加一个事件监听器,用于监听客户端从服务器接收内容时触发的sync
事件。