W3C

Web 中文兴趣组会议

2022年9月6日

题目:contenteditable 的讨论

讲者:黄烈锦(金山办公)[演示文稿]

现场纪要

黄烈锦:

大家好!我是黄烈锦,来自金山办公。

以下会从设计的初衷与优劣势等方面进行阐述技术选择。

我在金山大概有七年时间,经历了三个编辑器项目。我可以介绍一下我们的项目。之前金山在大家的印象里就是做WPS 和office相关产品。16年开始,我们为了追赶微软和Google的步伐,做了很多编辑器的项目。

我们做了“写得”“文字”“文档”,这些都是在线编辑项目,也是内容服务的工具。写得项目是我加入公司以来经历的第一个编辑器。后来,我们在17年、18年左右开始协作文字,我们是朝着比较高的维度设计,在中国在线编辑器补齐了在线 office的空白,我们采用了内核、排版的全链路自研。第三个是这两年我主要负责的项目,相对轻量级,特性区别于传统文档的协作项目,目前我们会把它叫Flexpaper或者协作文档。

我们从PPT里可以看到,日本或者东亚做了很多排版艺术相关的设计,文字排版也可以归类为一种艺术。

我们在Web上有什么呢?我们有Contenteditable这个组件。今天我们不会讨论具体传统的office,我们会从Contenteditable入手。

富文本以前的形式在软件时代以office为主。后来以Google为标志,Google 第一个以协作形式在Web上实现office,我们今天看到所见即所得这种形式的场景,在Web上以此能力集成的。但是,Web发展到今天,我们依旧面临很多问题,比如浏览器的生态、标准等等很多缺陷,这也是我们今天要讨论的Web Editing现有解决方案。

以下大概分为结构、命令、I/O和形态四个部分讨论。

背景:

首先,简单开发一个编辑器并不难。假如我们不需要很好的特性或者兼容度,用户体验差的情况下是可用的,在最低可用的情况下会很好实现。事实上这无法达到软件开发的标准和质量。那我们面临什么问题呢?

第一部分:结构

从模型到排版再到视图几个关联维度,可以看到这一小片段代码,一个粗体可以有接近四种形态,当然存储的时候Contenteditable可以自定义模型进行存储。目前,该组件在模型、排版、视图上会出现多对多对多的情况。

如何解决这个问题呢?很多厂家包括语雀、腾讯都会进行模型分离。

这里为什么要从存盘结构、内存结构、排版结构、视图结构四个维度讲呢?因为传统office的角度讲,存盘到最终排版展示,经历了一系列的过程,但最终不一定会采取这个分层方案。

第一是存盘,office的存盘结构是采用文档流或者说是二进制流,强调的是存储的大小或者说压缩率的角度。而这个格式以微软主导,我们公司也有自己的格式,但行业内最终都是以微软office为标准。

其次是分层,大多数做文档的设计会把排版和内存结构归纳为一个,无可厚非,其实分开也有它的好处。从初步解析出来的内存结构到排版缓存之间可以做很多优化。在某种分层方式中,排版到渲染也可以是完全和内核无关的过程,会涉及排版渲染优化,而且渲染技术手段在Web非常多样化,可选择方案很多,但逃不了的都是渲染优化API的技术限制。

在考虑第二个问题之前,我们先来看看结构化文档的优势,接下来我们看一个例子。

假如我们采用结构化文档(也是现在一些主流Web文档做的方式)。可以看PPT上类似的设计,首先是一个doc顶级节点,下面有FlexVessel节点,再是BasicVessel,其下包含Object节点。对比之下,office以段句属性为准,段句属性可以对应为此结构下的Segment或者是Paragraph。

我们通过这个Demo,可以看到,采用结构化有什么优势呢?

类ooxml。微软在大概十年前左右推出了ooxml的格式,这个格式的好处是可以更好地做业务数据交换,office类型的软件格式的文件可以兼容一些应用接入。我们知道xml也是一种数据格式,好处是非常方便扩展。

对比之下,如果我们采取二进制流

假设用一个二进制流进行扩展一个产品特性,那么我们需要从最底层存储出发去做,要设计存储在哪儿,再是如何解析、如何做I/O,如何从IO后的内存结构进行排版,最后渲染成某个节点形态,这条链路很长。

这也是之前讲的文字项目和文档项目最大的差异,我们最新的文档项目更有利于Web的扩展,我们是基于Web原生出发来做编辑器。

以上便是第二部分的模型,这是偏向架构层面的问题,也是我们如何思考富文本现在与未来的问题。具体地问题,比如我们应该采用同构还是异构的方式来做存储结构和模型,当然这可以视具体的业务场景来设计。

我们做的一个比较大的点,是实现通用节点,通用容器,这就类似扩展块节点的方式,尽量降低开发编辑器业务能力的难度。

整体上,我们的思维就是从原先底层写起的编辑器,逐渐提高业务虚拟化程度,让业务开发者很轻松的创建文档新的特性节点,这是我们的目标。即不用过多做排版相关或者渲染相关,这也是我们做通用节点的缘故。

最后是排版结构与渲染结构,主流产商都会做这一部分自研。

排版结构到渲染结构如何做约束?主要有两个场景,其中一个问题是解析排版渲染的时候,就是在编辑操作或者交互操作时候怎么做到渲染一致性的问题。我们通过Mutation来进行约束,具体不展开。

我们可以看一下Google文档的结构化,它的设计和我们差异很大,做的时间比较久,对标office365的格式,它的排版特性支持度高且复杂,比如这里有page、section,还有布局页和文本页,文本框之类。对于整个office支持的复杂性,从Google文档命名也可以窥探一二,它对标微软,基于Web的方式去做,尝试在Web上融合office的能力。

这是它的结构化节点,描述了一个表格的TableCell。TableCell比较复杂,有很多需要优化的地方。

刚才讲了Web和office融合,排版规则多样,我这里只提了“行禁则”,这也是照顾到现今看到的双链笔记和块编辑器等的新形态编辑器。虽然还有段落禁则、页禁则等诸多排版特性,与当前新形态的Web编辑器相互冲突,行禁则在现代编辑中对用户的体验依旧有很大的提升。

接下来可以看到中英文间距,右侧看一些标点符号,按照正常的排版,可能令人难受。加上行禁则排版优化,相对比较美观,包括后续的演讲者将讲到的中文排版设计,如何设计得美观,这是很设计师的事情。我们如何实现这个特性的。前面语雀的同学讲到了如何高亮优化,我们所有的高亮和划词评论都统称为区间数据。

那么我们采取区间树有什么好处呢?大家可以了解到,我们设计的区间树维持在时间复杂度相对平衡的水准。当然,区间树底层可以是红黑树,也可以有其他的实现方式。

区间树目前使用在批注、评论,协作归属,以及其他区间数据上。协作归属场景中,当每个人编辑文档或者编辑的段落,它就会实时计算那个区间属于谁,这个计算量很大,一篇文档的操作可能是十几二十万个,假设现在输入一段字,以一个时间间隔收集一个命令,比如几秒钟,文字上考虑时间间隔很短,如果提交一个段落,可能需要上百个命令,相对好一点可能是几十个,量也是很大,协作场景下,数量级会更大。这时候支持大数据量区间位置计算,区间树能很好地解决这个性能问题。

目前,我们是用JS版本,也考虑后续用WASM。我们希望W3C方面可以考虑一下在区间Range这个API上做一些突破,或者也可以参考一下。当然,原生C++性能是最好的。我们期待区间树和编号树(虽然有序编号、无序编号,每个厂商都不一样,但编号树可以做得比较通用,不涉及业务),可以考虑加入Web 的基础API。

第二部分,命令与交互

前面已经提出了交互的问题,IME问题是我认为最头疼,也是我们不能100%解决交互体验和交互一致性的事情。

举个例子,我输入一段文本,监听了所有事件,大家可以看到不同浏览器和不同的输入,有预输入和没预输入的情况下,有无拼音输入,中文厂商的输入法,产生的事件都是不同的,完全无法预知事件的一致性,这就是一个很难的点。

我们的做法就是去做一个中间的取值input,不管是预输入还是输入法如何交互,都预先读一个input做临时的存储,生成一个命令去写入。

移动端的IME的问题比较严重,举一个iOS的笔记,它有一个工具,不仅会有一个白条,甚至还有国产输入法厂商天马行空的设计。这时候,我们努力尝试解决这个问题,就是通过键盘弹起事件分析反向解决输入法和键盘的计算问题。即便如此,我们依旧不能百分之百解决所有兼容性难题,这就是为什么在移动端很多厂家会推荐用户去下载APP(比如飞书),因为APP比Web更加可控。

我也非常赞成W3C提出的虚拟键盘提议。

接下来是模拟选区层,大家看图示是怎么做到的[多媒体演示],基本上可以分为拦截层、点击测试和模拟交互渲染层,这三个层次可以构成模拟选区的方案,相信很多厂商都是这样做,各家的实现大同小异。其中比较复杂的是点击测试,点击测试需要付出了比较大的开发成本。

我们在Contenteditable投入的资源巨大,需要做重建,重建所有事件,所有点击测试,识别业务行为,写入内核,做内核的IO,最后通过事务分发管理器,再交回给渲染层重新渲染。

即使今天W3C提出了自己的标准,我个人认为它依旧需要考虑的事情是提高可定制性。在未来或者当前已经出现了各种新型编辑器,传统的创作工具,事实上已经落后于这些新工具的理念,未来它需要做的更多。如何提高API的可定制性呢?或者说low level api如何为新编辑器形态预留的扩展性。我认为这是比较大的命题。这也是我们想和大家讨论的趋势”块编辑器和流编辑器的关系”。

下面是命令,原生的方案是execCmomand,现在成了废弃的W3C的API,所以这个就不考虑。

既然我们选择了自建,怎么在架构上做设计呢?架构是通过一个业务去驱动的,本质上office也是一个业务驱动架构定型成docx的形式,它充满了各种各样厂家自有的设计。我们需要服务于我们需要服务的事情,那就是协作,编辑,块,记录等等。

协作是关键问题,这里我们分三层的好处,就是在维护和可扩展性方面比较好做。原子命令一般是insert和delete,有的只有一个replace;而我们所有的协作都是基于原子命令之上的operation,这对协作而言不可再分。最后可以划分命令层和事务层,事务层会真正涉及很多非写入命令,这两层可以合并在一起的。

第三部分:I/O

接下来就是另一个史诗级巨坑,复制粘贴和多格式。相信大家做文案编辑或者内容创作的时候,最常做的一件事就是复制粘贴。

在Web里,需要满足的复制粘贴量比较大,我们会通过非剪切板的方式,绕开了Web自己的能力。

从PPT中可以看到,Web复制粘贴的问题,异步和同步粘贴板的限制,有时会令人摸不着头脑。此能力与APP无法相比,APP想复制多少就复制多少,Web只能复制一张png。当然,这里也有安全的考量,通过限制类型,避免部分安全问题。我希望能够探讨这个事情。

特别提一下ipad的限制,当我们重建以后,有些问题会变得复杂。整体方案上,复制时会通过一个管理器读取文档数据,写入到剪切板;粘贴时写入格式,先从剪切板里读数据,写入到文本文档里。复制粘贴有Copy&Paste两个事件,最初我们也想继续用这个事件,但发现已经有很多限制,就只能自建一条路径,我们希望能够实现的复制粘贴交互方式可以自定义。

下面可以看导入导出的问题,复制粘贴和导入导出都是格式化I/O,我们提出了一个自己的解决方案,就是我们做一个基于自定义或者相对通用的形式去做一个通用交换格式。

第四:Web Editing形态

最后是发散讨论编辑器的辅助功能和未来形态。

我们已经自建了很多能力,采用了WebAssembly实现中文分词、拼写检查等等,这些辅助功能对于编辑器不可或缺。

形态上,流编辑器是非常有意思的。blockprotocol提出以后,至少是一个进入再自定义W3C新的编辑能力时候,是否在设计low level api时要考虑对新形态的支撑很重要。

第二是所见即所得(WYSIWYG)和所见即所视(WYSIWYM)如何融合的问题,所见即所视的编辑器需要有一个过程进行转化到现代编辑器。现在主流的做法就是用快捷命令或者即时渲染解决这个问题,目前我们正在探讨,我们也正在和产品一起发散和思考怎样的交互是既亲民又友好的交互。基本上在很多主流在线编辑器都已经实现。

第三是HTML和Word的格式,它们的理念天差地别,怎么做到两个理念融合,我认为不需要考虑两者当前所展现出来的版式和形式,也许有一天当有一个全新的编辑器出现时,我们就知道Web上的编辑器形式应该是什么样子了。

谢谢大家!

提问:我有一个问题,平常我用Google文档,也用微软365,它有一些比较常见的功能,有一些API可以控制这些东西,甚至打通这些office软件,像你们做的有没有考虑这些API的设计?比如JS来控制。

黄烈锦:这个问题很好。其实我们有可定制的api,但如果是数据上,刚才听到了云剪切,可以把它放开,叫做云数据服务。其实云剪贴板本质上依旧就是I/O的问题,就是格式与格式之间如何流转、流通和转化的问题。Google做的是搜索,它在Google Docs里有很多集成相关能力的设计。当编辑的时候是即时搜索、即时检测,其实从底层看更容易看出来,它是数据问题或者流转问题,也搀杂了产品问题,我们是从Word到PPT或者是思维导图,我们还支持第三方,前提是我们对他们的数据有开放的了解,双方要有互认或者双方要有合作关系。假设,两个产品线是割裂的情况下,双方是无法做到服务打通。比如第一个版本编辑器做到兼容Bear格式,但是下一个版本升级了就无法正常流转数据,这是我们不可预知的。

这件事是我们主动做的事情,不是在一个生态内的事情。这个生态可以不是一家厂商的事情,当然,也可以在W3C层面提出的问题,就是数据流转的交互格式,包括未来的块编辑器,做的不一定是数据,可以做到协作方式,块编辑,如何做到局部子编辑依旧可以加入协作,在Web形态下,可能Web3就可以解决问题。大致就是数据和交互两方面,如果有一个统一或者业界能达成一致的标准的话,就很好解决这个问题。谢谢!


返回[会议总结页面]获取其他话题的会议纪要。

若您对上述内容有任何疑问或需进一步协助,请联系:会议主办方 W3C 北航总部 <team-beihang-events@w3.org>。