中企动力建设的网站如何修改,网页设计培训(可0基础),黄骅港务局,湖北省建设工程造价信息网官网这是在线文档技术的第二篇文章#xff0c;本文将对目前市面上所有的主流编辑器和在线文档进行一次深入的剖析和研究#xff0c;从而使大家对在线文档技术有更深入的了解#xff0c;也让更多人能够参与其开发与设计中来。
注意#xff1a;出于对主流文档产品的尊重#xf…这是在线文档技术的第二篇文章本文将对目前市面上所有的主流编辑器和在线文档进行一次深入的剖析和研究从而使大家对在线文档技术有更深入的了解也让更多人能够参与其开发与设计中来。
注意出于对主流文档产品的尊重故在分析这阶段会比较简单旨在让开发者发现重点不会深入去解析其前端代码实现。
主流编辑器分析
Slate
架构简析
Slate 是一个完全可订制的富文本编辑器框架其所有的逻辑都是通过插件来实现的用户拥有高度的自由不会被 slate 多定制的规则所约束。
为什么要编写 slate作者在其编写文档中这样写道“在发明 Slate 之前我尝试了大量的富文本编辑器库我发现使用它们构建简单的 demo 是没有问题的但是当你开始构建类似于 MediumDropbox Paper 或者 Google Docs 这样的项目你会遇到深层次的问题编辑器的 “schema” 是硬编码的编程式转换文档是非常复杂的对 HTMLMarkDown 等内容的序列化支持看起来像是事后加上的重新发明一个视图层似乎是效率低并且有局限的协同编辑不是预先设计好的代码仓库是庞大的并非小而可复用的无法构建复杂嵌套的文档。”
在 Slate 的世界里插件是“一等公民”all in plugin可以通过插件定制所需要的任何功能而不受到约束同时 slate 保持了与 DOM 相同的数据模型这使得在 DOM 上能做的操作在 slate 中也可以实现slate 在设计之处就做了明确的边界划分将核心和定制版边界描述的十分清晰同时 slate 在设计之初就考虑到了协同使用者不需要在接入协同的时候去做彻底重构可以简单的实现接入。
Slate Core部分架构图如下所示 CreateEditor为外部可以直接调用的创建编辑器的方法常见实用操作如下所示。
const editor useMemo(() withHistory(withReact(createEditor())), [])上述代码就是创建了一个基于React渲染的且支持undoredo的编辑器我们可以通过创建方法发现其扩展功能的方法就是实现一个类似withReact的东西这个就是slate世界的一等公民“插件”。
Slate Core部分的代码非常的简洁仅仅提供了插件扩展能力以及基本的数据流操作能力其是默认不提供渲染层的这一点需要我们去进行编写当然作为一个前端工程师我相信这并不难。
接着深入createEditor方法我们会发现createEditor是一个完备的编辑器方法在内部实现了包括数据原子操作属性操作节点操作等编辑器所需的所有基础能力可以说其架构能力非常之强具体方法接口如下所示由于源码中作者为进行接口抽象这里为了减少代码量我这里直接用的Interface的方式进行展示。
// IBaseEditor slate的基础能力基本上都是对原子数据的操作
type interface IBaseEditor {apply(op: Operation); // 应用opaddMark(key: string, value: any); // 添加属性deleteBackward(unit: TextUnit);deleteForward(unit: TextUnit);deleteFragment(direction?: forward | backward);getFragment();insertBreak(): void; // 插入回车insertSoftBreak(): void; // 插入软回车insertFragment(fragment: Node[]); // 插入FragmentinsertNode(node: Node); // 插入节点insertText(text: string); // 插入文字removeMark(key: string); // 移除属性getDirtyPaths(op: Operation): Path[]; // 获取脏区
}插件扩展
既然我们说在Slate中插件是一等公民那么我们如何编写一个插件呢其方式也是非常的简单这里我直接copy一个官方React渲染插件进行说明。
继承扩展BaseEditor主要是自己定制化的能力。withReact实现接口定制的能力。最后withReact(createEditor());完成插件的使用。
// ReactEditor 扩展基础的编辑器能力
export interface ReactEditor extends BaseEditor {}export const withReact (editor: T,clipboardFormatKey x-slate-fragment
): T ReactEditor {// 实现对应的编辑器方法
};// 最后创建并使用编辑器
const editor withReact(createEditor())通过上述操作我们就可以完成一个基于Slate的插件开发工作。
基本数据结构
在 slate 官网中提到其拥有近似于浏览器 DOM 的 API其模型为基于 DOM 的一颗嵌套树其命名事件定义均符合浏览器标准能够让开发者很轻易的理解和上手同时也降低了浏览器处理变更产生的成本。
export type Node Editor | Element | Text;
export interface Element {children: Node[];[key: string]: unknown;
}
export interface Text {text: string;[key: string]: unknown;
}Element 类型含有 children 属性可以作为其他 Node 的父节点Editor 可以看作是一种特殊的 Element 它既是编辑器实例类型也是文档树的根节点Text 类型是树的叶子结点包含文字信息
用户可以自行拓展 Node 的属性例如通过添加 type 字段标识 Node 的类型paragraph, ordered list, heading 等等或者是文本的属性italic, bold 等等来描述富文本中的文字和段落。
其中[key: string]: unknown用于属性添加。
原子操作类型
Slate提供的原子操作类型有9种涵盖了文字处理节点处理选区处理等。 Prosemirror
Prosemirror的设计就更改的模块化了不像是Slate数据层的处理是在一起的Prosemirror所有的模块都被剥离为独立的模块数据OpTransformstateView都独立存在。其设计思路高度定制化使得用户的代码可以完全控制项目让开发更像是乐高积木而不是一个整体的汽车。
架构解析
Prosemirror的核心模块主要分为四部分开发者可以独立维护各自部分。
prosemirror-model定义了编辑器的基础模型用于描述编辑器内容的数据结构。prosemirror-state提供描述编辑器整个状态的数据结构类似于Context的玩意儿。prosemirror-view视图层提供一个用户组件界面使得用户可以直接操作编辑。prosemirror-transform包含以可以记录和重放的方式修改文档的功能基础编辑能力的实现这是模块中事务的基础state并且使撤消历史记录和协作编辑成为可能。
大概架构设计如下所示
View为渲染的视图层State为编辑器状态管理编辑器的核心能力都在这里可以实现类似于Context的玩意儿Transform和Model咱们就不细细分析了大家的思路大差不差。 同时在数据结构的设计上Prosemirro也是遵循了和浏览器DOM相似的原则具体图如下所示图片来自于Prosemirror官方文档。 Ace
相比前面的Prosemirror和SlateAce的完成度就相当的高了代码量也相当的庞大。其特征包括来自github/ace的readme文件
Syntax highlighting for over 120 languages 支持超过120种语言的语法高亮Over 20 themes 支持超过20种主题Automatic indent and outdent自动缩进的能力An optional command line 支持可选命令Handles huge documents 能够处理巨大的文档Fully customizable key bindings including vim and Emacs modes可定制快捷键Search and replace with regular expressions支持使用正则表达式搜索和替换Highlight matching parentheses突出显示Toggle between soft tabs and real tabs选项卡切换Displays hidden characters字符的隐藏和显示Drag and drop text using the mouse支持鼠标拖拽文本Line wrapping换行Code folding代码折叠Multiple cursors and selections多光标和选择Live syntax checker语法检查Cut, copy, and paste functionality复制粘贴
通过上面的的特征描述我们可以毫不客气的说ace编辑器只需要简单改改就可以直接进行商业化此编辑器已经具备的完备的能力。
因为篇幅关系我这里就不再向前两个一样详细分析了如果要做产品我会比较推荐这个。
主流在线文档产品分析
腾讯文档
腾讯文档是基于多人实时在线编辑技术的文档协作与文件共享平台。同时提供包含帐号、品类、盘、管理后台、API、安全等能力与企业内部系统进行无缝集成从而实现自动化文档工作流。
因为腾讯文档的品类众多所以我们暂时只分析其在线编辑器部分首先我们先通过Chrome的搜索能力搜索一些关键词看看其有么有使用开源的编辑器。通过DOM我们可以发现腾讯文档的编辑器不同于前面所分析的各种编辑器而是采用的Canvas渲染至于这种渲染的优劣我也不太好评价。 我们通过前端页面渲染的DOM可以发现虽然这里展示主要是依赖Canvas渲染其还是有部分是挂的DOM至于原因我也不知道估计是其业务比较复杂有些能力Canvas无法支持所以选择外挂的形式进行补充。
打开Chrome的控制台选择Network我们会发现腾讯文档的数据更新与同步是采用WS的形式进行传递的这一点和Google Doc不同GoogleDoc是采用轮询API的方式进行同步的。 金山云文档
金山文档是由珠海金山办公软件有限公司发布的一款可多人实时协作编辑的文档创作工具软件。 [1] 金山文档可应用于常见的办公软件如文字Word、表格Excel、演示PPT。 金山文档做了两套在线文档Doc我们先分析第一个。 这个在线文档我们很容易在节点中发现其使用了开源编辑器Prosemirror基本是按照Prosemirror的结构来展示的。 在数据交换上金山文档和腾讯文档类似都采用WS的方向进行通讯其中一个WS通道负责心跳检测另一个负责数据交换。
飞书文档
飞书文档作为后起之秀其设计用户体验已经逐渐超越腾讯文档和金山文档同样老规矩我们先看他用的啥编辑器。打开控制台我们会发现ace字样我们推测其使用的是ace编辑器同样通过控制台我们可以看到其数据结构也是个树形。 其中飞书的后台提交和其他几家不同飞书采用的是cgi的提交方式这一点和Google很像。
谷歌文档
谷歌文档作为在线文档界的鼻祖那在行业的积累那是相当炸裂的体验也是几家最好的。google和腾讯文档一样也采用的canvas渲染据说这个canvas渲染google内部研究了好几年作者对比了一下腾讯文档和google文档的体验毫不客气的说google做的cavnas渲染比腾讯文档好很多倍。
其次Google文档前端的混淆也是相当的厉害几乎达到了完全不可读的程度。当然也不是完全没办法。
打开控制台我们通过如下命令可以获取到Google编辑器的数据结构
window.KX_kixApp虽然可读性很差但是慢慢debug总体还是可以查看的。当然如果大佬们有时间可以通过映射字符还原文档比如如下这种。 Google的数据提交和飞书一样是采用轮询CGI的方式进行提交数据结构如下所示为了方便理解我在下方做了一定的解释。
[{commands:[{ty:is, // Operation类型ibi:11, // 插入坐标s:1 // 插入内容}],sid:151f773a331f21bc,reqId:1}
]没错is就是GoogleDocs的一个Op我们可以通过同样的方式继续挖掘google 的op设计从而完成产品的分析。
对比
产品编辑器渲染类型数据提交方式协同算法采用框架腾讯文档CanvaswebsocketOT算法自研Canvas引擎金山文档DOMwebsocketOT算法Prosemirror飞书文档DOMCGI轮询OT算法Ace谷歌文档CanvasCGI轮询OT算法自研Canvas引擎
通过上述表格我们就可以知道各家的产品都是啥样了。
最后
最后希望大家能在我的文章中有所收获欢迎交流。
参考链接
https://www.kdocs.cn/latest?fromdocs 金山文档
https://ace.c9.io/#navembedding Ace编辑器
https://prosemirror.net/ Prosemirror编辑器
https://www.slatejs.org Slate源码