docs/(plugins)/(collaboration)/yjs.cn.mdx
Y.Doc。UnifiedProvider 接口添加自定义提供者(例如用于离线存储的 IndexedDB)。RemoteCursorOverlay 用于渲染远程光标。cursors 自定义光标外观(名称、颜色)。init 和 destroy 方法来管理 Yjs 连接生命周期。安装核心 Yjs 插件和您打算使用的特定提供者包:
npm install @platejs/yjs
用于 Hocuspocus 服务器端协作:
npm install @hocuspocus/provider
用于 WebRTC 点对点协作:
npm install y-webrtc
import { YjsPlugin } from '@platejs/yjs/react';
import { createPlateEditor } from 'platejs/react';
const editor = createPlateEditor({
plugins: [
// ...otherPlugins,
YjsPlugin,
],
// 重要:使用 Yjs 时跳过 Plate 的默认初始化
skipInitialization: true,
});
配置插件的提供者和光标设置:
import { YjsPlugin } from '@platejs/yjs/react';
import { createPlateEditor } from 'platejs/react';
import { RemoteCursorOverlay } from '@/components/ui/remote-cursor-overlay';
const editor = createPlateEditor({
plugins: [
// ...otherPlugins,
YjsPlugin.configure({
render: {
afterEditable: RemoteCursorOverlay,
},
options: {
// 配置本地用户光标外观
cursors: {
data: {
name: 'User Name', // 替换为动态用户名
color: '#aabbcc', // 替换为动态用户颜色
},
},
// 配置提供者。所有提供者共享同一个 Y.Doc 和 Awareness 实例。
providers: [
// 示例:Hocuspocus 提供者
{
type: 'hocuspocus',
options: {
name: 'my-document-id', // 文档的唯一标识符
url: 'ws://localhost:8888', // 您的 Hocuspocus 服务器 URL
},
},
// 示例:WebRTC 提供者(可与 Hocuspocus 一起使用)
{
type: 'webrtc',
options: {
roomName: 'my-document-id', // 必须与文档标识符匹配
signaling: ['ws://localhost:4444'], // 可选:您的信令服务器 URL
},
},
],
},
}),
],
skipInitialization: true,
});
render.afterEditable: 指定 RemoteCursorOverlay 来渲染远程用户光标。cursors.data: 配置本地用户的光标外观,包括名称和颜色。providers: 要使用的协作提供者数组(Hocuspocus、WebRTC 或自定义提供者)。RemoteCursorOverlay 需要在编辑器内容周围有一个定位容器。使用 EditorContainer 组件或 platejs/react 中的 PlateContainer:
import { Plate } from 'platejs/react';
import { EditorContainer } from '@/components/ui/editor';
return (
<Plate editor={editor}>
<EditorContainer>
<Editor />
</EditorContainer>
</Plate>
);
Yjs 连接和状态初始化需要手动处理,通常在 useEffect 钩子中完成:
import React, { useEffect } from 'react';
import { YjsPlugin } from '@platejs/yjs/react';
import { useMounted } from '@/hooks/use-mounted'; // 或您自己的挂载检查
const MyEditorComponent = ({ documentId, initialValue }) => {
const editor = usePlateEditor(/** 前面步骤中的编辑器配置 **/);
const mounted = useMounted();
useEffect(() => {
// 确保组件已挂载且编辑器已就绪
if (!mounted) return;
// 初始化 Yjs 连接、同步文档并设置初始编辑器状态
editor.getApi(YjsPlugin).yjs.init({
id: documentId, // Yjs 文档的唯一标识符
value: initialValue, // 如果 Y.Doc 为空时的初始内容
});
// 清理:组件卸载时销毁连接
return () => {
editor.getApi(YjsPlugin).yjs.destroy();
};
}, [editor, mounted]);
return (
<Plate editor={editor}>
<EditorContainer>
<Editor />
</EditorContainer>
</Plate>
);
};
生命周期管理: 您必须调用 editor.api.yjs.init() 来建立连接,并在组件卸载时调用 editor.api.yjs.destroy() 来清理资源。
</Callout>
访问提供者状态并添加事件处理器来监控连接:
import React from 'react';
import { YjsPlugin } from '@platejs/yjs/react';
import { usePluginOption } from 'platejs/react';
function EditorStatus() {
// 直接访问提供者状态(只读)
const providers = usePluginOption(YjsPlugin, '_providers');
const isConnected = usePluginOption(YjsPlugin, '_isConnected');
return (
<div>
{providers.map((provider) => (
<span key={provider.type}>
{provider.type}: {provider.isConnected ? '已连接' : '已断开'} ({provider.isSynced ? '已同步' : '同步中'})
</span>
))}
</div>
);
}
// 为连接事件添加事件处理器:
YjsPlugin.configure({
options: {
// ... 其他选项
onConnect: ({ type }) => console.debug(`提供者 ${type} 已连接!`),
onDisconnect: ({ type }) => console.debug(`提供者 ${type} 已断开。`),
onSyncChange: ({ type, isSynced }) => console.debug(`提供者 ${type} 同步状态: ${isSynced}`),
onError: ({ type, error }) => console.error(`提供者 ${type} 错误:`, error),
},
});
使用 Hocuspocus 的服务器端协作。需要运行中的 Hocuspocus 服务器。
type HocuspocusProviderConfig = {
type: 'hocuspocus',
options: {
name: string; // 文档标识符
url: string; // WebSocket 服务器 URL
token?: string; // 认证令牌
wsOptions?: HocuspocusProviderWebsocketConfiguration; // 高级 websocket 配置(headers、协议等)
}
}
wsOptions您可以传递 wsOptions 字段来配置 Hocuspocus 提供者的高级 websocket 选项。这对于自定义 headers、认证、协议或 HocuspocusProviderWebsocket 支持的其他 websocket 设置非常有用。
示例用法:
{
type: 'hocuspocus',
options: {
name: 'my-document-id',
},
wsOptions: {
url: 'ws://localhost:8888',
maxAttempts: 5,
parameters: {
// 请求参数
}
},
}
使用 y-webrtc 的点对点协作。
type WebRTCProviderConfig = {
type: 'webrtc',
options: {
roomName: string; // 协作房间名称
signaling?: string[]; // 信令服务器 URL
password?: string; // 房间密码
maxConns?: number; // 最大连接数
peerOpts?: object; // WebRTC 对等选项
}
}
通过实现 UnifiedProvider 接口创建自定义提供者:
interface UnifiedProvider {
awareness: Awareness;
document: Y.Doc;
type: string;
connect: () => void;
destroy: () => void;
disconnect: () => void;
isConnected: boolean;
isSynced: boolean;
}
在 providers 数组中直接使用自定义提供者:
const customProvider = new MyCustomProvider({ doc: ydoc, awareness });
YjsPlugin.configure({
options: {
providers: [customProvider],
},
});
为服务器端协作设置 Hocuspocus 服务器。确保提供者选项中的 url 和 name 与服务器配置匹配。
WebRTC 需要信令服务器进行对等发现。公共服务器可用于测试,但生产环境应使用自己的服务器:
npm install y-webrtc
PORT=4444 node ./node_modules/y-webrtc/bin/server.js
配置客户端使用自定义信令:
{
type: 'webrtc',
options: {
roomName: 'document-1',
signaling: ['ws://your-signaling-server.com:4444'],
},
}
配置 TURN 服务器以获得可靠连接:
{
type: 'webrtc',
options: {
roomName: 'document-1',
signaling: ['ws://your-signaling-server.com:4444'],
peerOpts: {
config: {
iceServers: [
{ urls: 'stun:stun.l.google.com:19302' },
{
urls: 'turn:your-turn-server.com:3478',
username: 'username',
credential: 'password'
}
]
}
}
}
}
认证与授权:
onAuthenticate 钩子验证用户token 选项传递认证令牌传输安全:
wss:// URL 进行加密通信turns:// 协议配置安全的 TURN 服务器WebRTC 安全:
password 选项进行基本的房间访问控制安全配置示例:
YjsPlugin.configure({
options: {
providers: [
{
type: 'hocuspocus',
options: {
name: 'secure-document-id',
url: 'wss://your-hocuspocus-server.com',
token: 'user-auth-token',
},
},
{
type: 'webrtc',
options: {
roomName: 'secure-document-id',
password: 'strong-room-password',
signaling: ['wss://your-secure-signaling.com'],
peerOpts: {
config: {
iceServers: [
{
urls: 'turns:your-turn-server.com:443?transport=tcp',
username: 'user',
credential: 'pass'
}
]
}
}
},
},
],
},
});
检查 URL 和名称:
url(Hocuspocus)和 signaling URL(WebRTC)是否正确name 或 roomName 在所有协作者之间完全匹配ws://,生产环境使用 wss://服务器状态:
网络问题:
独立实例:
Y.Doc 实例name/roomName 使用唯一的文档标识符ydoc 和 awareness 实例编辑器初始化:
skipInitialization: trueeditor.api.yjs.init({ value }) 设置初始内容内容冲突:
Y.Doc覆盖层设置:
RemoteCursorOverlayEditorContainer 或 PlateContainer)cursors.data(名称、颜色)设置正确YjsPlugin使用 Yjs 启用实时协作,支持多个提供者和远程光标。
<API name="YjsPlugin"> <APIOptions> <APIItem name="providers" type="(UnifiedProvider | YjsProviderConfig)[]"> 提供者配置数组或预实例化的提供者实例。插件将从配置创建实例并直接使用现有实例。所有提供者将共享同一个 Y.Doc 和 Awareness。每个配置对象指定提供者 `type`(例如 `'hocuspocus'`、`'webrtc'`)及其特定的 `options`。自定义提供者实例必须符合 `UnifiedProvider` 接口。 </APIItem> <APIItem name="cursors" type="WithCursorsOptions | null" optional> 远程光标配置。设置为 `null` 可显式禁用光标。如果省略,当指定了提供者时默认启用光标。传递给 `withTCursors`。参见 [WithCursorsOptions API](https://docs.slate-yjs.dev/api/slate-yjs-core/cursor-plugin#withcursors)。包括 `data`(本地用户信息)和 `autoSend`(默认 `true`)。 </APIItem> <APIItem name="ydoc" type="Y.Doc" optional> 可选的共享 Y.Doc 实例。如果未提供,插件将在内部创建一个新实例。如果与其他 Yjs 工具集成或管理多个文档,可以提供您自己的实例。 </APIItem> <APIItem name="awareness" type="Awareness" optional> 可选的共享 Awareness 实例。如果未提供,将创建一个新实例。 </APIItem> <APIItem name="onConnect" type="(props: { type: YjsProviderType }) => void" optional> 任何提供者成功连接时触发的回调。 </APIItem> <APIItem name="onDisconnect" type="(props: { type: YjsProviderType }) => void" optional> 任何提供者断开连接时触发的回调。 </APIItem> <APIItem name="onError" type="(props: { error: Error; type: YjsProviderType }) => void" optional> 任何提供者遇到错误时触发的回调(例如连接失败)。 </APIItem> <APIItem name="onSyncChange" type="(props: { isSynced: boolean; type: YjsProviderType }) => void" optional> 任何单个提供者的同步状态(`provider.isSynced`)发生变化时触发的回调。 </APIItem> </APIOptions> <APIAttributes> <APIItem name="_isConnected" type="boolean"> 内部状态:是否至少有一个提供者当前已连接。 </APIItem> <APIItem name="_isSynced" type="boolean"> 内部状态:反映整体同步状态。 </APIItem> <APIItem name="_providers" type="UnifiedProvider[]"> 内部状态:所有活动的、已实例化的提供者实例数组。 </APIItem> </APIAttributes> </API>api.yjs.init初始化 Yjs 连接,将其绑定到编辑器,根据插件配置设置提供者,可能用初始内容填充 Y.Doc,并连接提供者。必须在编辑器挂载后调用。
<API name="editor.api.yjs.init"> <APIParameters> <APIItem name="options" type="object" optional> 初始化的配置对象。 </APIItem> </APIParameters> <APIOptions type="object"> <APIItem name="id" type="string" optional> Yjs 文档的唯一标识符(例如房间名称、文档 ID)。如果未提供,将使用 `editor.id`。对于确保协作者连接到相同的文档状态至关重要。 </APIItem> <APIItem name="value" type="Value | string | ((editor: PlateEditor) => Value | Promise<Value>)" optional> 编辑器的初始内容。**仅当共享状态(后端/对等端)中与 `id` 关联的 Y.Doc 完全为空时才应用此内容。**如果文档已存在,其内容将被同步,忽略此值。可以是 Plate JSON(`Value`)、HTML 字符串或返回/解析为 `Value` 的函数。如果省略或为空,当 Y.Doc 是新的时将使用默认的空段落进行初始化。 </APIItem> <APIItem name="autoConnect" type="boolean" optional> 是否在初始化期间自动为所有配置的提供者调用 `provider.connect()`。默认:`true`。如果您想使用 `editor.api.yjs.connect()` 手动管理连接,请设置为 `false`。 </APIItem> <APIItem name="autoSelect" type="'start' | 'end'" optional> 如果设置,初始化和同步后自动聚焦编辑器并将光标放置在文档的"开头"或"结尾"。 </APIItem> <APIItem name="selection" type="Location" optional> 初始化后要设置的特定 Plate `Location`,会覆盖 `autoSelect`。 </APIItem> </APIOptions> <APIReturns type="Promise<void>"> 初始设置(包括可能的异步 `value` 解析和 YjsEditor 绑定)完成时解析。注意提供者连接和同步是异步进行的。 </APIReturns> </API>api.yjs.destroy断开所有提供者的连接,清理 Yjs 绑定(从 Y.Doc 分离编辑器),并销毁 awareness 实例。必须在编辑器组件卸载时调用以防止内存泄漏和过时连接。
api.yjs.connect手动连接到提供者。当 init 期间使用了 autoConnect: false 时很有用。
api.yjs.disconnect手动断开提供者连接。
<API name="editor.api.yjs.disconnect"> <APIParameters> <APIItem name="type" type="YjsProviderType | YjsProviderType[]" optional> 如果提供,仅断开指定类型的提供者连接。如果省略,断开所有当前已连接的提供者。 </APIItem> </APIParameters> </API>