富文本编辑器 wangEditor v5 渲染逻辑 - Model ,DOM,HTML 三者的关系

wangEditor v5 版本仍然在公开测试中,可以查阅文档demo,或者提交 issue 。v5 预计 2022 年春季正式发布。

image.png

背景

最近 2 周多,我一直专心搞 wangEditor “回显 HTML” 功能,即创建 editor 时可通过 HTML 初始化内容。已经发布上线了(可以参考相关文档),支持 JSON ,支持 HTML (包括 V4 的)。

const editor = createEditor({
  selector: '#editor-container',
  // content: [...],
  html: '<p>hello&nbsp;<strong>world</strong></p>',
  
  // `content` (JSON 格式) 和 `html` (HTML 格式) ,二选一
})
复制代码

在这之前, V5 初始化内容只支持 JSON 格式,不支持 HTML 格式。这给用户带来了很多困扰

  • 往往需要同时存储 JSON 和 HTML ,造成数据冗余
  • 不能将 V4 升级为 V5 ,因为 V4 是完全基于 HTML 格式的

当初我还为此写了一篇长长的文档,详细介绍了应该如何获取内容、如何存储、如何转换等... —— 我写着也麻烦,大家看着也麻烦。所有需要刻意解释的东西,都是不美的。美的东西天生就简洁,从来不用解释,大家一看就会。所以支持 HTML 之后,我就把那篇又臭又长的文档给删了。

搞完了“回显 HTML”之后,wangEditor 的渲染逻辑基本也就闭环了,所以趁机做一次总结。
不仅仅是这次“回显 HTML”的,而是 wangEditor 整个渲染逻辑的总结。

之前为何执着于 JSON 格式?

其实从 V5 开始公开测试,就有用户提出要支持 HTML 格式。之前我是一直拒绝的,后来提出的人多了,我才慢慢意识到这个事儿重要性。

之前我一直拒绝使用 HTML 格式的理由,主要有以下两点。

slate.js 只有 JSON 格式

wangEditor v5 是基于 slate.js 为内核进行开发的,slate.js 输入输出的数据一直是 JSON 格式,没有 HTML 格式。所以,我也就基于这一点,一直坚持用 JSON 格式数据。

但后来我才慢慢想明白,slate.js 能基于 JSON 格式,本质原因是它完全依赖于 React 框架,使用 React 来渲染页面。而 React 本身就是一个“数据驱动视图”的框架,所以 slate.js 提供 JSON 作为数据,交给 React 来渲染视图,这很合理。

而 wangEditor 虽然基于 slate.js 为内核,但它没有基于 Vue React 等任何框架。既然没有框架要求,那就需要自己去考虑渲染逻辑,那也就需要 HTML 。

HTML 格式太灵活多变,不能完全支持

富文本编辑器本身就是一个复杂度很高的软件,这就要求它内部的数据结构要精简、规范、统一,不能乱来。

很不幸,HTML 格式就是“乱来”的代名词。例如,我要写一个文本 hello 并实现加粗,我要是放开了写,能有无数种写法,请上眼:

<b>hello</b>
<strong>hello</strong>
<span style="font-weight: bold;">hello</span>
<span><b>hello</b></span>
<p style="font-weight: bold;"><span>hello</span></p>
<div><strong>hello</strong></div>
<!-- ...还有更多... -->
复制代码

如果编辑器允许用户输入 HTML (既然是用户输入,那就无法控制输入的格式),这么多格式,都要支持吗?—— 成本太高了。而且,这可是富文本编辑器,要考虑选区、各种样式修改等... 总之,数据结构一旦复杂了,再结合富文本编辑器本身的复杂度,那将是无法想象的。

而使用 JSON 格式就好说了,我可以自己定义 JSON 格式的规范,做成一个 DSL 。
JSON 定义好了大家是不会随意改动的,因为 DSL 的规则他们不一定全懂。但 HTML 就不好说了,用户也会用 HTML ,说不定就随意改动一下源码,自己加一点样式什么的,不好控制。

但是呢,对 HTML 的支持又是势在必行的,所以这个问题我想了好久,最终得出一个虽不完美但可行的方案:HTML 仅支持 wangEditor 输出的格式(包括 V4 和 V5),其他的格式就保证了。我也把它重点写到了文档里

image.png

JSON 数据,是什么

JSON 数据就是编辑器的 Model 。可以把编辑器认为是一个黑盒,输入输出的可以是 HTML ,但其内部数据是规定格式的 JSON 数据。

image.png

例如,对于普通的一行文字,HTML 和 JSON 分别如下:

image.png

现代富文本编辑器的 Model 都是基于一种 DSL 的,不会直接基于 HTML 。因为 HTML 格式过于灵活,不好控制,而自己的 DSL 是完全可控的。

Model 渲染到编辑器

进入编辑器内部,Model(即 JSON 数据)需要实时的渲染到编辑器视图中,即把 Model 渲染为 View 。而且不同的组件要渲染为不同的 DOM 类型,如 <p> <blockquote> <ul> <a> <video> 等。

image.png

数据驱动视图,这一点和 Vue React 一样,所以我们也做了类似的设计:第一,把 Model 转换为 vdom ;第二,把 vdom patch 到真实 DOM 中(使用 snabbdom.js)。这么一对比,这里的 Model 就相当于 Vue 中的 data ,React 中的 state 。

但是还要考虑各个组件渲染为不同的 DOM 类型,所以需要让各个模块注册自己的 Render 函数,再统一渲染。
如果你要开发第三方组件,也需要用到这些 Render 函数,可参考文档

image.png

Model 生成 HTML

编辑器 Model 生成并输出 HTML ,不同的组件需要生成不同的 HTML 格式。也需要各个模块注册自己的 toHtml 函数,再统一生成 HTML。
如果你要开发第三方组件,也需要用到这些 toHtml 函数,可参考文档

image.png

把 HTML 转换为 Model

用户输入 HTML 转换为 Model (JSON 数据),不同的组件需要生成不同的 JSON 格式。也需要各个模块注册自己的 parseHtml 函数,再统一生成 Model 。
如果你要开发第三方组件,也需要用到这些 parseHtml 函数,可参考文档

image.png

总结

到现在基本说清楚了 Model DOM HTML 三者的关系,整体逻辑如下图:

image.png

最后,关于 wangEditor v5 的任何使用问题,欢迎提交 issue

猜你喜欢

转载自juejin.im/post/7055206586175717390