乐趣区

关于angular:slateangular-正式开源

本文由智能化研发管理工具 PingCode 前端工程师 杨振兴 分享

一句话介绍

slate-angular 是一个基于 Angular 和 Slate.js 的编辑器视图层,帮忙开发者应用 Angular + Slate.js 构建 Web 富文本编辑器。

Slate.js 介绍

Slate.js 是一个特地优良的富文本编辑器框架,代码整洁、架构良好、扩展性强,目前市面上基于 Slate.js 开发的富文本编辑器及产品曾经不可胜数。而且 Slate.js 足够有魔力,从开始接触富文本编辑器开发到当初两年左右的工夫,感觉 Slate.js 始终是我成长的良师,从最后的入门调研,到开发出第一个版本编辑器,而后是视图层架构的降级,再到前面的性能及稳定性的优化,每一个阶段都能从 Slate.js 身上学到很多货色,当然对它的理解也越来越深。我感觉 Slate.js 就是一个本身在一直地迭代和改良的编辑器框架,专一于视图层和模型层,架构实现始终紧跟社区支流技术的倒退,内容编辑的实现机制也在一直地向现行标准事件聚拢,曾经开源 5 年左右的工夫,目前社区依然十分沉闷,还在一直地扩大更多的实用场景,是十分厉害的一个存在。

自研 Angular 视图层

咱们的前端技术栈是 Angular,开源社区中基于 Angular 开发的富文本编辑器极其稀缺,而咱们要做的 PingCode Wiki 产品又对富文本编辑器的易用性和可扩展性要求极高,调研发现 Slate 编辑器框架十分合乎咱们的需要,外围模型层不依赖任何前端框架,可扩展性极其高,有残缺的测试笼罩,所以咱们尝试自研基于 Angular 的视图层,能够了解为自研视图层的外围驱动来源于产品。

集体感觉开发基于 Angular 的视图层是一个很有意义的过程,并且以后开源社区中还没有应用 Angular 开发 Slate 编辑器的实现,所以咱们想把咱们的这种实际成绩开源进来,以回馈 Slate 和 Angular 社区,让更多的开发者能够基于 Angular + Slate 开发富文本编辑器。

开源之路

以后 slate-angular 曾经反对企业级知识库产品 PingCode Wiki 稳固运行超过 1 年,最后版本是基于 Slate@0.47.0 版本(JavaScript 版本 Slate),第二版是基于最新 Slate 的实现(TypeScript),以后曾经是第三版,从年初就开始筹备,包含公开 Github 仓储、对立底层的实现及 API 格调、搭建在线 Demo、补充单元测试、同步降级最新 Slate 等。

Demo 性能:

github:https://github.com/worktile/s…
demo:http://slate-angular.ngnice.com/
slate-angular 可能不是一个开箱即用的富文本编辑器,它属于 Slate 框架的一个视图层,对接 Slate 框架底层与用户交互界面的一个中间层,它仅提供根底的富文本编辑能力和性能扩大的插槽,但这也是 slate-angualr 的劣势,它只专一底层的实现:兼容浏览器、挪动端、代理输出事件、代理光标、反对自定义 Element/Text/Leaf 节点的渲染、解决根底交互等,基于它能够很快开发出像 Quill、Prosemirror 这类有肯定根底性能的富文本编辑器,并且它的可扩大下限很高,通过肯定的工夫齐全能够构建出像 Confluence、Notion、PingCode Wiki 这种性能级别的富文本编辑器。

个性反对

  • 集成块级元素前后光标计划
    反对扩大指定元素渲染前后光标,不便在块级元素前后插入段落(表格 Demo 中曾经实现块级光标的根本交互)
  • 自定义组件 / 模版渲染块级元素
    反对多层级元素内容的自定义渲染,可不便实现像表格这类简单场景的需要,同时维持了自定义组件之间的正确的依赖注入链,也就是单元格组件能够通过依赖注入获取父级表格组件的服务
  • 自定义渲染 Text
    反对自定义 Text 节点的内容渲染,这个在 slate-react 中是不提供的,slate-angular 中独自提出来了,次要用于实现加粗、斜体、下划线、色彩、背景色等需要
  • 自定义渲染 Leaf
    反对自定义 Leaf 节点的渲染,Leaf 是对 Text 的拆分,每一个 Text 节点默认对应一个 Leaf,而 Text 节点拆分 Leaf 的根据是 Decoration 装璜器,次要用于实现对文本的动静润饰,配合自定义 Leaf 组件实现搜寻高亮、划词评论等需要
  • Decoration 装璜器
    提供对文本内容的动静润饰,是由内部数据驱动定位、装璜文本内容。它的特点是在不扭转原始数据的状况下实现对文本的装璜,是解决动静需要的一种形式
  • Void 元素
    Void 意为不可编辑,所有内容属于一个整体,slate-angular 反对扩大 Void 元素,通过 Void 元素能够把任意简单的 Angular 组件的嵌入到编辑器中,比方图片、代码编辑器、甘特图等都能够嵌入到编辑器内容区域中。

兼容浏览器
Chrome、Edge、Safari、Firefox、QQ Browser

已解决常见 Slate.js 兼容问题
中文输出反复问题
中文输出解体
Safari 浏览器输出中文焦点跳动
\n 导致内容凌乱
a 标签 导致内容凌乱
表格构造束缚问题
angular comment 问题

这些问题以后曾经在 slate-angular 中失去解决,在此不做更多的阐明,如有任何疑难欢送给 slate-angular 提 Issues。

技术路线

接下来聊点技术相干的内容,集体其实始终想对 Slate.js 的架构设计以及外部机制进行一些分析,所以借助这次开源跟大家简略聊聊 slate.js 和 slate-angular 的技术路线以及比拟重要的一些底层机制。

先从 Slate 架构说起:

Slate 框架外围次要蕴含模型层和视图层,模型层定义形容富文本内容的根本数据结构(一个反对嵌套的节点树)和对该数据的根底操作,视图层对接前端框架,解决根底输出行为、选区代理,内容渲染、插件扩大等等。

值得一提的是 Slate 的数据模型定义都是都是参照 DOM 规范实现的,对老手还算敌对,比方数据模型的概念 Block、Inline-block、Text 等都跟 DOM 中的意义统一,选区也一样有 Selection、Anchor、Focus、Collapsed 等概念。

Slate 富文本编辑器架构概貌:

基于 Angular 开发富文本编辑器

slate-angular 作为一个独立的视图层,是 Slate 底层与下层性能实现之间的桥梁,核心作用就是施展框架的劣势,更好的组织编辑器性能的开发。

基于 slate-angular 开发富文本编辑器能够说是原汁原味的 Angular 滋味,无论是根底性能批改、还是扩大新性能,你都能够应用 Angular 组件或者服务组织代码的实现,而不再是应用组件对 JavaScript 库进行简略的封装:

  1. 你能够应用 Angular 组件或者模板自定义插件的渲染。
  2. 你能够基于 Angular 组件封装简单的交互行为。
  3. 你能够复用 Angular 组件库。
  4. 你也能够应用服务在父子级节点组件之间共享数据,它们维持了正确的依赖注入链。

总之 Angular 的所有个性你都能够应用。

内容编辑根底原理

以后技术框架下想实现对输出内容的管制大略有两种实现思路,一种是事件代理,另外一种是监控内容变动,Slate 次要采纳的是事件代理,就是通过监控一系列内容输出的 DOM 事件,而后通过事件类型及其它上下文判断该输出对应的数据操作,最初把它转化为针对数据模型的一系列操作。

监控内容变动的形式在 Slate 中也有用到,是为了反对 Android 浏览器,大略是因为 Android 浏览器下某些场景的输出事件无奈被正确捕捉,进而无奈响应用户的操作,所以应用 MutationObserver 监控内容变动以正确响应用户的输出行为。

因为视图层中事件代理的实现次要是与输出事件打交道,各个浏览器对于输出事件的实现不齐全对立,加上又要辨别一般英文输出和中文组合输出,所以须要针对不同浏览器做很多兼容性解决,能够说打的是一套组合拳,招式概览:

  1. 现实状况下:应用 beforeinput 事件实现根底输出代理,因为 beforeinput 语义化清晰,能够作为输出行为判断规范。
  2. 非现实状况:浏览器不反对 beforeinput 事件,应用 React 的合成事件 onBeforeInput 解决英文输出(Angular 中须要本人实现),对于其它输出交互如回车、删除应用 keydown 事件处理。
  3. IME 输出解决应用事件 compositionstart 和 compositionend 解决,这三个事件十分牢靠,没有任何浏览器兼容性问题。
  4. 除此之外撤销 / 重做、焦点挪动等是在 keydown 事件中解决,复制、剪切等逻辑应用原生 copy、cut 事件即可,而粘贴、拖拽等逻辑和根底输出一样依赖 beforeinput 事件,如果浏览器不反对 beforeinput 事件则在 paste、drop 等事件中解决。

事件代理过程概貌:

选区同步机制

和浏览器的选区一样,Slate 的数据模型也须要选区,当数据变更产生时标识数据批改的地位,并且这个地位须要跟浏览器原生的选区保持一致,无论是浏览器的选区变动了,还是 Slate 的选区变动了都须要实现相互同步。

上面介绍下 slate-angular 视图层中选区的双向同步机制:

一、DOM Selection -> Slate Selection:

监控原生 Document 对象的 selectionchange 事件,当 DOM Selection 扭转时查问对应的 Slate Selection,批改 Slate Selection 与 DOM Selection 统一。

交互行为 -> DOM Selection 扭转 -> selectionchange -> sync Slate Selection
交互行为包含鼠标 Click、按方向键等

二、Slate Selection -> DOM Selection:

Slate 数据 Change 导致 Slate Selection 发生变化,须要在 Change 事件中做解决,依据最新的 Slate Selection 查问对应的 DOM Selection,批改 DOM Selection 与 Slate Selection 统一。

交互行为 -> 触发数据更新 -> 新的 Slate Selection -> 视图刷新 -> sync DOM Selection

插件扩大

Slate 应用插件来扩大编辑器性能,并且插件是一等公民(slate-angular 也能够了解为是一个根底插件),任何高级交互都能够通过开发编辑器插件来实现。

一、可重写办法

Slate 底层通过形象出一个一个的可重写办法(deleteBackward、insertBreak、insertText、apply 等等)供内部扩大,比方我要实现粘贴时辨认 Markdown 数据格式,能够重写 insertText 实现,实现回车的非凡解决能够重写 insertBreak,相比间接裸露根底事件,提供可重写形式是一个很高级的实现,在 slate-angular 视图层也独自提供了几个可重写的办法:insertData(解决粘贴数据)、isBlockCard(块级卡片)、onError(错误处理)、onKeydown(根底事件)。

二、自定义渲染

视图层 UI 局部次要由三层渲染组成,对应三个层级的数据:Element、Text、Leaf,每一个层级的数据都反对自定义组件 / 模版渲染,次要是通过 renderElement、renderText、renderLeaf 实现。

视图层自定义渲染组件的过程概貌:

这部分次要介绍跟 slate-angular 关联比拟大的几个局部:组件化开发编辑器、事件代理、选区同步、插件扩大等,外围还是心愿大家能够更多的理解 slate.js 以及 slate-angular,技术上就点到为止,有趣味的能够浏览源代码或者其它技术材料。

写在最初

富文本编辑器是前端中非常复杂的一个畛域,将来的路线还很长,咱们也心愿有更多的开发者可能参加进来,发现并解决诸如浏览器兼容性、兼容挪动端、中文输出、规范交互的问题,优化输出代理的机制,优化底层架构,摸索基于 Slate 的协同计划等等。

如有任何问题,欢送大家给 slate-angular 提 Issues 或者 PRs!

退出移动版