如何快速实现多人协同编辑?

如何,快速,实现,协同,编辑 · 浏览次数 : 545

小编点评

**摘要** 本文介绍了一种针对在线协同编辑场景的优化方法,该方法可用于解决一些在命令传递后信息丢失的问题。该方法首先通过监听所有客户端的操作,并将命令发送到服务端。服务端仅转发命令,并根据命令执行相应的操作。 该方法还处理了粘贴粘贴、编辑状态、行列变动等多种协同场景,并针对这些场景使用了定制化的处理逻辑。 **关键点** * 使用命令Manager监听所有客户端的操作,并将命令发送到服务端。 * 服务端仅转发命令,并根据命令执行相应的操作。 * 对编辑状态、行列变动等多种协同场景使用定制化的处理逻辑。 * 使用命令传递后信息丢失的场景进行特殊处理。

正文

摘要:本文由葡萄城技术团队于博客园原创并首发。转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。

引言

协同编辑是目前成熟的在线文档编辑软件必备的功能,比如腾讯文档就支持多人协同编辑,基本都是采用监听command,然后同步此command给其他客户端来实现的,例如以下系列:

https://gcdn.grapecity.com.cn/showtopic-82517-1-1.html
https://gcdn.grapecity.com.cn/showtopic-82518-1-1.html
https://gcdn.grapecity.com.cn/showtopic-82519-1-1.html

这种做法可以快速实现大部分功能的协同操作,但是也有一些不足,我大致将这些不足分为两种类型:

第一种,command传递之后信息丢失,需要重写或修改command的,比如复制粘贴功能。

这种类型对应的是希望command生效,但实际上没有生效。

第二种,多人协同所必须的特殊功能,情况比较多:

1. 比如编辑一个单元格时,其他人不允许编辑此单元格,并有样式提醒;

2. A用户正在编辑时,B用户在上方插入了一行,此时A编辑的单元格也要下移,而不是保留在原位;

3. 缩放时不对其他页面有影响;

这种类型对应的是不希望command生效,或者希望改变command生效的效果。

如果你也在做协同,并且遇到了上述问题,那么这篇文章或许可以解答你心中的疑问。

先看下最终的实现效果吧:

在开始前,先对demo的架构做一个说明,我此次写的demo是html做前端,并用nodejs做服务端,前后端通信采用websocket的方式,目录结构如下:

大家在测试demo前,请务必认真阅读readme文件。

下面我就讲一下如何针对上面提到的几种情况做优化,以更好得满足协同的需求,整体的思路其实比较简单,无非就是对那些不满足需求的command做拦截,单独处理。以上提到的情况并不包含实现协同所需的全部功能,只是抛砖引玉,如果有其他没有考虑到的情况,可以用同样的方法处理。

一、向所有客户端同步command

这里用commandManager新增监听的方式来监听所有的操作,并用websocket发送到服务端。马赛克部分为后续其他代码逻辑,暂时不用看。

服务端仅做一个转发:

其他客户端接受到此消息,执行command即可:

到这里,开头提到的快速实现大部分操作的协同就已经完成了,后续的操作都是为了弥补当前方案的不足。

二、处理粘贴

粘贴的command同步到其他客户端时,会执行失败,仔细对比发出的command和接收的command,会发现其中两个字段发生了变化:

这两个数组内部本应该是Range对象,但是却被转换成了不同的Object,这是由于我们使用了JSON.stringify方法,而用此方法序列化时并不支持Range对象,所以我们在客户端接受到此信息时,需要重新将其还原为Range:

其实你可能会发现当存在fromRanges的时候,我直接用了copyTo方法实现了粘贴,并没有重新执行command,效果其实是一样的。

这里还隐含这另一种情况:从外部复制内容,粘贴到spread,这时fromRanges对象是不存在的,那么我们就需要执行command了,当然执行之前要把pastedRanges数组的值变为Range类型。

三、编辑状态唯一

即同一个单元格同一时间只能有一个用户编辑。这是协同编辑几乎必备的一个需求,看起来很简单,但事实上是比较复杂的。当客户端有用户开始编辑时,向服务端发送消息,

而服务端需要维护一个数组,记录所有当前正在被编辑的单元格信息,并向所有客户端同步

其他客户端收到消息后,用户如果要编辑此单元格,则禁止用户进入编辑状态

当然,用户可能希望看到有哪些人正在编辑哪些单元格,类似于这种效果:

这里是用自定义单元格的方案实现的:

这个功能算是初步实现了,但是考虑一下这种情况:如果你正在编辑时,其他用户在上方插入了一行呢?

Lily本来正在编辑A2,Alen在上方插入一行后,Lily应该编辑的是A3,但是以我们目前的实现方式,Lily编辑的仍然是A2。对应的,在上方删除行、在左侧插入删除列都会有同样的问题。

这里Lily和Alen两个人都会受到影响,Lily编辑的单元格应该移动,Alen被锁定的单元格也应该移动,而Alen这边比较简单,服务端根据插入行列更新锁定单元格信息就好,Lily这边则麻烦一些,需要记录下Lily已经输入的功能,并且在新的单元格打开,并开启输入框,其中callback函数就是选择新的输入框的逻辑,根据不同的状态有所不同,所以用回调函数的形式实现。

四、行列变动同步

相信你也注意到,在上述处理中,行列的变化信息是很重要的,在原生command的基础上还要有编辑框的处理逻辑,所以行列的变化也需要我们单独来处理,在客户端收到行列变化的消息时, 做出拦截:

并对编辑的框做出正确的移动

结语

到这里,这篇文章也接近尾声了,整体实现的思路其实比较简单,无非就是拦截那些不符合协同需求或者同步时有问题的command,并重新实现它们。这种方式能够快速实现简单的协同,并且做出定制化的修改。

但是这种方式也存在着一些问题,比如无法支持undo堆栈,你可以在代码中看到我会随时清空undo堆栈,阻止用户进行undoredo操作,这是因为用commandManager.execute的方式执行command时,一定会进入到堆栈,这就导致A用户的操作会出现在B用户的undo堆栈中,B用户撤销时,就有可能撤销A用户的操作。

除了上面这个问题以外,一定还有其他更深、更棘手的问题存在,所以要在实际项目中实现协同,我的想法是根据业务限制用户的操作类型,并对这些有限的操作针对性地开发协同功能,这样虽然效率比较低,但是由于涉及面小,更便于控制。

OK,以上就是这篇文章的全部内容了,欢迎读者在评论区留下你们的想法~

简单协同代码下载链接:

https://gcdn.grapecity.com.cn/forum.php?mod=attachment&aid=MjgzMDk0fGVlNTFkNGQ1fDE2OTA3NjM4Mjl8MHwxNzY0MDU%3D

扩展链接:

Spring Boot框架下实现Excel服务端导入导出

项目实战:在线报价采购系统(React +SpreadJS+Echarts)

Svelte 框架结合 SpreadJS 实现纯前端类 Excel 在线报表设计

与如何快速实现多人协同编辑?相似的内容:

如何快速实现多人协同编辑?

> 摘要:本文由葡萄城技术团队于博客园原创并首发。转载请注明出处:[葡萄城官网](https://www.grapecity.com.cn/),葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。 ## 引言 协同编辑是目前成熟的在线文档编辑软件必备的功能,比如腾讯文档就支持多人协同编辑,

Azure DevOps(一)基于 Net6.0 的 WPF 程序如何进行持续集成、持续编译

一,引言 我们是否正在为如何快速的编译、部署客户端应用程序而烦恼?这也是博主最近遇到的问题。目前博主所在公司主要做项目级的定制化开发,多以 C/S 架构的 WPF 程序为主,每次到了协助开发团队给实施团队编译好的要测试程序包时,就会出现多人协助,编译、打包好的二进制程序包 pull 最新代码 ,以及

玩转服务器之数据传输篇:如何快速搭建FTP文件共享服务器

FTP服务(File Transfer Protocol,FTP)是最早应用于主机之间数据传输的基本服务之一,是目前使用最广泛的文件传送协议。FTP文件共享服务器在日常办公中可以实现多人之间文件的传递和共享,极大提高协同办公的效率

一键启动的AI离线知识库,无需复杂环境依赖,小白都能上手了

简介 在人工智能技术飞速发展的今天,我们经常面临一个挑战:如何快速、简便地部署和使用AI技术?AntSK项目,一个开源的AI知识库和智能体,就是为了解决这一问题而诞生的。现在,我们自豪地宣布,AntSK已经实现了无需复杂部署的一键启动功能,让每个人都能轻松拥抱AI的便利。 为什么选择AntSK? 无

Grafana 系列文章(十二):如何使用Loki创建一个用于搜索日志的Grafana仪表板

概述 创建一个简单的 Grafana 仪表板, 以实现对日志的快速搜索. 有经验的直接用 Grafana 的 Explore 功能就可以了. 但是对于没有经验的人, 他们如何能有一个已经预设了简单的标签搜索的仪表板,以帮助一些团队在排除故障时快速找到他们正在寻找的东西。虽然 Explore 很适合这

直播预约|漫漫用户增长之路,如何快人一手

![](https://oscimg.oschina.net/oscnet/up-7fdd8d6d86b797b2041a2cda61ede5145c2.jpg) **【导读】** 如何实现用户增长及提升用户活跃度是各大开发者关注的重点之一,日常运营过程中,消息推送作为一个触达用户的有效手段,通过恰

华夏天信携手华为云开天aPaaS,打造安全、高效、节能的主煤流运输系统

摘要:基于开天aPaaS集成工作台,主煤流运输系统如何实现多源异构数据融合、皮带物料和人员违章的智能感知,以及皮带的智能控制。灵活架构、高效集成、快速开发! 本文分享自华为云社区《华夏天信携手华为云开天aPaaS,打造安全、高效、节能的主煤流运输系统》,作者:开天aPaaS小助手。 据权威数据显示,

Mac上使用Royal TSX快速连接到OCI主机

**问题:** 每次使用Royal TSX连接到OCI主机都要交互式输入opc这个用户名,次数多了也蛮烦。 那如何既指定用户名,又想要通过ssh私钥登陆机器呢? 这个需求确实很初级,但也着实困扰过我,因为开始我真的以为不支持,认为这两种连接方式只能选其一。结果没想到人家是可以组合使用实现这样的需求。

如何基于 Kubernetes 实现优质开发者平台体验?

内部开发者平台(或 IDP)是使开发团队能够更快、更轻松、更一致地交付应用程序的基础设施。Kubernetes 本身是一个功能强大的平台,但它引入了太多复杂性和功能,因此不能简单地将其作为 IDP 交给开发团队。若要期望他们能取得成功,非常重要的一点是要设置一些防护措施,使他们能够有效地使用 K8s

云小课|云小课教您如何选择Redis实例类型

阅识风云是华为云信息大咖,擅长将复杂信息多元化呈现,其出品的一张图(云图说)、深入浅出的博文(云小课)或短视频(云视厅)总有一款能让您快速上手华为云。更多精彩内容请单击此处。 摘要:购买Redis实例时,实例类型有单机、主备、Proxy集群、Cluster集群和读写分离这么多种,该怎么选?别担心,本