实时协作功能集成
本快速入门指南将指导您在编辑器中设置实时协作,包括与其他协作功能的基本集成。
阅读完本文后,我们建议您访问我们文档页面中的“了解更多”部分,了解评论、跟踪更改和修订历史记录功能,以获取更深入的知识。
作为本指南的补充,我们提供了可供下载的可立即使用的示例。我们为所有类型的编辑器(包括多根编辑器)以及 React、Angular 和 Vue.js 集成准备了示例。您可以将它们用作示例或集成起点的起点。
# 注册协作服务
实时协作功能需要服务器来同步客户端之间的内容,因此要使用它,您需要先注册协作服务。有关更多详细信息,请参阅CKEditor 云服务协作 – 快速入门指南。此外,还有可用于测试目的的高级功能免费试用,它将提供必要的后端。
# 准备自定义编辑器设置
要使用实时协作,您需要准备一个启用了多个功能的自定义编辑器设置。
最简单的方法是使用Builder。选择一个预设并开始自定义您的编辑器。
Builder 允许您选择您喜欢的分发方式和框架。在本指南中,我们将使用“Vanilla JS”选项,并使用“npm”和一个基于“Classic Editor (basic)”预设的简单设置,以及启用的实时协作功能。
在 Builder 的“功能”部分(第 2 步)中,确保
- 确保“协作”组旁边的“实时”切换处于打开状态,
- 选择整个“协作”功能组。
完成设置后,Builder 将为您提供必要的 HTML、CSS 和 JavaScript 代码片段。我们将在下一步使用这些代码片段。
# 设置示例项目
拥有自定义编辑器设置后,我们需要一个简单的 JavaScript 项目来运行它。为此,我们建议您从我们的存储库中克隆基本项目模板
npx -y degit ckeditor/ckeditor5-tutorials-examples/sample-project sample-project
cd sample-project
npm install
然后,安装必要的依赖项
npm install ckeditor5
npm install ckeditor5-premium-features
此项目模板在幕后使用Vite,并包含我们将使用的 3 个源文件:index.html
、style.css
和 main.js
。
现在是时候使用我们的自定义编辑器设置了。转到 Builder 的“安装”部分并复制生成的代码片段到这 3 个文件中。
# 配置和初始化编辑器
要完成编辑器配置,您需要在 main.js
文件中执行一些额外的步骤
-
在编辑器配置(
editorConfig
)中,您需要填写以下字段- CKEditor 云服务连接数据 (
cloudServices
), collaboration.channelId
值。- 有效的
licenseKey
。有关详细信息,请参阅许可证密钥和激活指南。
- CKEditor 云服务连接数据 (
-
此示例使用看门狗功能,该功能在编辑器崩溃时提供额外的保护层以防止数据丢失。使用
Watchdog.create()
代替ClassicEditor.create()
来利用此功能,如下例所示。
如果您还没有准备好 CKEditor 云服务 URL,请首先在CKEditor 云服务协作 – 快速入门指南中了解有关这些 URL 的更多信息。
const editorConfig = {
/* ... */
licenseKey: '<YOUR_LICENSE_KEY>',
cloudServices: {
tokenUrl: '<YOUR_CLOUD_SERVICES_TOKEN_URL>',
webSocketUrl: '<YOUR_CLOUD_SERVICES_WEBSOCKET_URL>'
},
collaboration: {
channelId: 'your-unique-channel-per-document'
},
/* ... */
};
const { EditorWatchdog } = ClassicEditor;
const watchdog = new EditorWatchdog( ClassicEditor );
// Replace ClassicEditor.create(document.querySelector('#editor'), editorConfig); with
watchdog.create( document.querySelector( '#editor' ), editorConfig );
瞧!所有打开此页面的人都应该能够协作,同时处理同一篇富文本文档。
# channelId
配置属性
config.collaboration.channelId
配置属性是一个重要的属性,它控制哪些编辑器实例相互协作。使用相同 channelId
创建的所有客户端将协作处理相同的内容。
每个文档都必须拥有不同的 channelId
。此 ID 通常是数据库中文档的主键或给定表单字段的唯一标识符。但是,您可以自由提供适合您场景的任何标识符。
要开始协作新文档,您需要从一开始就拥有文档 ID,即当编辑器首次创建时。如果您的应用程序稍后创建文档 ID,例如在提交表单时,您可能需要提供另一种方法来生成文档 ID(例如,某些随机唯一 ID 生成器)。
channelId
在给定环境中需要是唯一的,因此,如果您使用的是例如暂存环境和生产环境,您可以在其中使用相同的通道 ID。由于环境是分开的,这些 ID 不会相互干扰。
由于编辑器内容与通道 ID 相关联且仅与通道 ID 相关联,因此可以创建一个应用程序,其中不同的视图(表单、子页面等)提供各种富文本可编辑字段,并且用户即使打开了不同的表单等,也可以相互协作。
# 处理文档数据
所有评论、建议和修订数据都保存在 CKEditor Cloud Services 中,以使集成开箱即用。
在本节中,假设 Cloud Services 文档存储 未启用。
文档存储 具有许多优点,我们建议您在您的情况下启用它。
请注意,CKEditor Cloud Services 不会保存或加载文档内容。相反,Cloud Services 充当处理协作过程的媒介。内容管理应由应用程序集成处理。
您公司的政策可能要求您将所有数据存储在您自己的数据中心或私有云中。我们理解这一点。这就是我们还提供 CKEditor Cloud Services 的本地版本的原因。联系我们 了解更多信息。
# 数据初始化
当第一个用户打开特定文档 ID 的富文本编辑器时,他们的内容将被发送到 CKEditor Cloud Services。在上述示例中,它是
<p>Let's edit this together!</p>
如果您无法或不想在 HTML 中直接设置初始数据,请改用 initialData
配置选项。不要使用 editor.setData()
(见下文)。
在第一个用户初始化文档并开始编辑会话后,每个连接到同一文档的其他用户都将从 CKEditor Cloud Services 接收内容(同时其本地初始数据将被丢弃)。
从那时起,对编辑器内容的每一次更改都会在 Cloud Services 和客户端之间传递,并且每个客户端的文档都会更新。任何冲突的更新都会在运行时解决。
这就是您在使用协作插件时不应使用 editor.setData()
或 editor.data.set()
方法的原因。这些方法只是覆盖编辑器的全部内容(即使要设置的数据与当前编辑器数据相同)。在实时协作中,这种行为会导致覆盖其他客户端的本地更新。在大多数情况下,在实时协作中使用 editor.setData()
和 editor.data.set()
是不正确的。
如果您确定您了解并接受以这种方式设置编辑器数据的行为和副作用,您可以使用标志 suppressErrorInCollaboration
设置为 true
调用 editor.data.set()
editor.data.set( '<p>Your data</p>', { suppressErrorInCollaboration: true } );
这将允许您覆盖编辑器数据,而不会引发错误。
# 保存数据
尽管 CKEditor Cloud Services 处理客户端之间的数据传递,但您仍然负责将最终数据保存到您的数据库中。文档仅由 CKEditor Cloud Services 临时存储在云中,只要有连接的用户。这意味着编辑器内容应该在最后一个用户断开连接之前保存在您服务器上的数据库中,否则它将丢失。建议您在内容发生更改时自动保存数据。
CKEditor 5 提供了两个实用程序,使您的集成更加简单。
# cloudDocumentVersion
属性
当您的协作用户同时保存同一文档时,可能会发生冲突。可能会发生一个用户试图保存较旧的文档版本,从而覆盖较新的版本。
为了防止此类竞争条件,请使用 cloudDocumentVersion
属性。
editor.plugins.get( 'RealTimeCollaborationClient' ).cloudDocumentVersion
此属性只是一个数字,较大的值表示较新的文档版本。此数字应与文档内容一起存储在数据库中。当客户端想要保存内容时,应比较文档版本。只有在版本更高时才应保存文档。否则,这意味着传入数据是文档的较旧版本,应将其丢弃。
CKEditor Cloud Services 保留 cloudDocumentVersion
的值,并确保在给定文档的每次新编辑会话时正确设置它。
# Autosave
插件
对大型文档使用自动保存功能可能会导致大量数据发送到您的服务器,并可能对编辑器用户体验产生负面影响。
我们建议您改用 文档存储 功能。
第二个助手是 Autosave
插件。
自动保存插件在用户更改内容时触发保存回调。它负责限制回调执行以限制对数据库的调用次数。它还自动保护用户在内容保存之前离开页面。
自动保存插件默认情况下未启用。要了解有关此插件的更多信息,请参阅 自动保存 功能指南。
# 自动保存修订历史记录
如果您使用的是修订历史记录功能,您的自动保存回调应负责更新最近的修订数据。您可以将 autosave
配置添加到 main.js
文件中的 editorConfig
中,如下所示
const editorConfig = {
/* ... */
autosave: {
save: async editor => {
const revisionTracker = editor.plugins.get( 'RevisionTracker' );
const currentRevision = revisionTracker.currentRevision;
const oldRevisionVersion = currentRevision.toVersion;
// Update the current revision with the newest document changes.
await revisionTracker.update();
// Check if the revision was updated.
// If not, do not make an unnecessary call.
if ( oldRevisionVersion === currentRevision.toVersion ) {
return true;
}
// Use the document data saved with the revision instead of the editor data.
// Revision data may slightly differ from the editor data when
// real-time collaboration is involved.
const documentData = await revisionTracker.getRevisionDocumentData( revisionTracker.currentRevision );
// Use revision version instead of `cloudDocumentVersion`.
const documentVersion = currentRevision.toVersion;
// `saveData()` should save the document in your database.
// `documentData` contains data for all roots.
// You can save just `documentData.main` if you are using a single root,
// or the whole object if you are using multiple roots.
return saveData( documentData.main, documentVersion );
}
},
/* ... */
}
您可以在 修订历史记录 指南中阅读有关保存修订的更多信息。
# 重新连接过程
在实时协作期间,可能会发生互联网连接一段时间内断开。发生这种情况时,编辑器将切换到只读模式。连接恢复后,实时协作插件将尝试将编辑器重新连接回编辑会话。此重新连接过程可能涉及重新创建编辑会话(如果重新连接发生在较长时间后)。
当一个客户端脱机时,其他客户端可能会对文档进行更改。当脱机客户端重新连接时,实时编辑插件将决定是否可以重新连接。
# 用户选择
实时协作编辑功能不仅在所有参与者之间同步文档内容,而且还实时向每个用户显示所有其他用户的列表。它开箱即用,不需要任何额外的配置。这是实时协作编辑插件中提供 UI 的唯一部分。请参阅 实时协作中的用户 指南以了解更多信息。
# 实时协作示例
请访问 ckeditor5-collaboration-samples
GitHub 存储库以找到实时协作功能的各种示例集成。
我们每天都在努力使我们的文档保持完整。您是否发现了过时的信息?是否缺少某些内容?请通过我们的 问题跟踪器 报告。
随着版本 42.0.0 的发布,我们重新编写了大部分文档,以反映新的导入路径和功能。我们感谢您的反馈,这将帮助我们确保其准确性和完整性。