美团的知识管理系统-学城,其富文本编辑器是基于prosemirror来实现的,我在其中开发了内部团队接入版本及当前正在开发的block化新版本。富文本涉及到的东西实在太多太深,在这个领域仅仅能算初来乍到,有理解的错误和说的不对的,大家多多指正。

prosemirror的schema系统还是非常强大,主要分为marks及nodes两类, 整个文档是由nodes构成的,schemaSpec定义了节点自身类型(inline / block等)及其所能包含的nodes类型(content),marks是对inline content比如(text*)的一种修饰例如加粗、斜体,两者的关系有点像胳膊和胳膊上的纹身。整体构成了文档的骨骼-schema,可以非常灵活的以颗粒度非常精细的方式自由定义文档结构和解析方式。但是pm有一个默认的设定:inline和block在schemaSpec的content中是不能mixed的, 一个典型的例子就是list标签的tab嵌套,这要求li的content是ul这类block级别的schema - list,所以哪怕是纯文本的输入,也必须加上paragraph这类block级别的schema - paragraph。content="(paragraph | list)+"导致文档结构如下:

纯文本的li标签下必须有paragraph标签,这会导致类似列表居中在内的一些布局因为这层paragraph异常。

另外notion的block设计一定程度上可以用prosemirrror来实现,discuss中也是有一些讨论Block structure editor by prosemirror​discuss.prosemirror.net

decoration、nodeview及toDOM

渲染层是富文本编辑器很重要的一层,在prosemirror中非文档内容的渲染由decoration层完成,而文档内容的渲染由toDOM或者nodeview来提供,toDOM适合轻量的展示如blockquote、link等,对于复杂如calendar之类的节点,nodeview的能力就体现出来了,毕竟谁也不喜欢在toDOM里写一大堆的冗余的无法理解的节点关系,可惜的是prosemirror更偏向于原生js开发,并没有针对框架的适配层,所以适合渲染复杂节点的nodeview与react之类的框架需要开发者自行集成,好在atlaskit、remirror及tiptap在这方面有一些工程实践的。 对于Decoration层,widget、inline及node的api能非常便利的处理placeholder等简单渲染,在复杂节点渲染上还是不得不依靠reactDOM.render等方式hack或者以直接以node+特定attribute的方式去代替decoration实现比如上传附件在内的中间态。

另外decoration的contenteditable=false属性还非常容易带来浏览器的各类光标问题,overlap的decoration的互相感知能力不足。decoration的事件机制缺乏,导致在decoration在更多的场景下仅仅能处理非常简单的渲染。

光标系统

麻烦的光标系统。。。prosemirror的indexing系统官网有个说明

是一套基于整型的下标索引,在整个html标签和textcontent层面精细化的索引系统,如果希望获取基于pos位置的详细信息,通过Node.resolve能获取到该pos下的详细信息。由于富文本编辑的过程就是在文本的什么位置做了什么操作,所以编辑过程中所涉及到的插入,拖入,拖拽,拖出,光标选区,复制,粘贴,macro等都需要pos的信息。pos => Node,pos => Dom, pos => resolvedPos, 都具备强大的api,但是Node作为编辑器对文档节点的描述,Node节点实例并不直接包含实例的pos。所以prosemirror mutation API几乎都以pos和dom为参数,posByNode很多时候不得不接触child,forEach,descendants等api转化,如果能支持互相的映射会极大的便利开发。pos <==> Node.

prosemirror中的选区有textSelection,nodeSelection,allSelection,充分的支持了选区range的不同情况。尽管在contentDOM这类边界情况下的选择还有一些奇怪的非预期行为(部分来自浏览器,部分来自prosemirror),总体是完善简单直接的,配合schema中的spec还能丰富节点的各种行为。即使如此,代码中还是不可避免的会有pos + 1, pos - 1等不怎么优雅的代码。咋说呢。。。pos +- n, n > 2就做好注释吧。。

由光标产生另一个问题就是,prosemirror是基于plugin/extension去开发的,各个plugin/extension可以利用很多的工程化能力让他们解耦和可插拔,在cursor这类基础功能插件中,如果真的有很多自定义特定node的边界光标要处理,还是要尽量避免耦合,这可能不是一件很难的事,但是是一件很麻烦的事。

Plugin system

plugin系统极大的丰富和扩展了编辑器的行为和能力。大部分情况下开发者也是以plugin来区分功能边界来开发的。

除了定义鼠标键盘行为以外,每个plugin可以维护自己的state,这有点类似react中state的感觉。对于prosemirror来说,setMeta/getMeta/getState/apply 等API可以方便的修改和获取state,并且是可以跨越plugin去操作的。但是有一个明显的感觉是,react中setState及单向数据流让组件状态的修改和影响范围可控又较易于理解,然而在prosemirror中这种基于链式的插件事件流体系下,配合上view层及nodeView层,使得状态的变更起始点和终结点不直观,对于复杂的插件,比如键盘唤醒的可输入搜索菜单,基于plugin state的维护终究还是略显杂乱。不过prosemirror plugin系统带来了极大的开发和设计自由度,并不完全是缺陷。

剪切/复制/粘贴

复制的之后的走向分为对内粘贴和对外部粘贴两类,由于上述的schema及parseDOM的解析规则,对内粘贴剪切板slice只要携带必要信息就会自动重走pm的解析逻辑,类似二次加载,所以不存在展示和适配问题。但是对外粘贴的兼容(如复制到word,wps等)如果仅仅基于clipboardhtml就很难做到适配和兼容了,比如富文本编辑器中的附件卡片,如果复制到word中下载事件和卡片本身可能就无法被word理解解析了。这就涉及到对剪切板的修改过程让内容同时满足对内对外的支持,prosemirror的这个能力主要来自clipboardSerializer层,形式上等同于schemaSpec的toDOM,这一层可以理解成在复制场景的特殊转移规则,可以想象,一个形态为

attachment filename。 prosemirror的ClipboardSerialilzer API的设计很好的补充丰富了schema。prosemirror在设计上很多的API既可以实现在plugin也可以直接整合至EditorView, 在不同的维度上很方便的支持了场景化的需求。总体来说,prosemirror对剪切/复制等行为的支持程度还是不错的。

实时编辑

基于crdt算法的yjs对prosemirror提供了y-prosemirror库,在prosemirror编辑器中的操作都被映射成yjs的type比如yxml的变化,客户端ydoc的变化在各端同步op,作用于各端的ydoc进而变成prosemirror的transaction。相关的另一个方案就是prosemirror-collab 官方库,prosemirror是一套mvc的模式,对state的修改触发view的渲染,修改state的方式为dispatch transacation,有些类似redux的工作方式。

collab库就是基于transaction的各端同步,区别在于yjs的ydoc接管了文档的形态,基于op/update还原文档的操作历史而不是传统的json或者htmlstring,这使得prosemirror的tojson在yjs方案下的实时编辑编辑器中变得略显鸡肋,也让server(非node)端对文档的掌控力大大削弱,比如清洗文档id,过滤不和谐内容等等变得复杂。

另一方面,基于transaction的方式,编辑器能在操作维度上做到非常细粒度的控制,例如version或者操作历史等方面。在没有实时编辑的版本,我写了一个基于结果(base、 remote、 local)的automerge合并逻辑以替代之前的合并冲突逻辑,但是这种基于结果的历史版本对比仅仅能在内容的差异上完成对比,颗粒度最小也仅仅能在childNodes级别,如果涉及到Node.TEXT_NODE,由于基于结果diff本身的颗粒度限制,在string层面实现单字节这种操作变更展示几乎不太可能,但是如果借助yjs的update或者pm‘transaction,就能在单用户维度上完整展示编辑历史和操作过程。

这种区别本质上是把文档视为某个确定时间点上的状态,还是一个时间轴上的操作集合。基本上,实时编辑在用户体验上无疑更好,但本质更是对非实时文档无法解决的问题的一个解决方案。

编辑器和浏览器的默认行为

hex2rgb、br标签,unicode等问题在富文本编辑过程中也是常见问题,部分来自浏览器默认处理行为,部分来自prosemirror本身的逻辑限制。

能想到的大概是这些,又想到的再补充吧。。

h是什么意思 富文本辑器_主流的开源「富文本编辑器」都有什么缺陷?相关推荐

  1. 【Linux系统】开发工具(上) {软件包管理器yum,更新yum源,文本编辑器vim,vim的四种基本模式,vim指令集,代码编译器gcc/g++}

    一.软件包管理器:yum 1.1 yum是什么? Linux下软件的安装方法: 源码安装:下载源码,在本地自行编译,然后才能安装. rpm安装:红帽软件包管理工具,属于安装包安装. yum安装:本身会 ...

  2. 拦截器获取请求参数post_「SpringBoot WEB 系列」RestTemplate 之自定义请求头

    [WEB 系列]RestTemplate 之自定义请求头 上一篇介绍了 RestTemplate 的基本使用姿势,在文末提出了一些扩展的高级使用姿势,本篇将主要集中在如何携带自定义的请求头,如设置 U ...

  3. 开源音乐播放器_如何选择开源音乐播放器

    开源音乐播放器 Linux提供了大量的音乐播放器. 您如何选择使用哪一个? 早在2016年6月,我就写了我最喜欢的开源音乐播放器Guayadeque显然逝世的文章. 我描述了我对Guayadeque真 ...

  4. 【推荐】1657- 灵活可扩展,2023年值得尝试的13款富文本编辑器

    作为前端开发人员,我们经常需要为网站和应用程序添加文本内容.与传统的文本编辑器不同,富文本编辑器可让您轻松创建各种类型的文本内容,包括加粗字体.斜体字.框架.列表.图片和视频等. 本文我将向大家推荐 ...

  5. 文本编辑器_markdown编辑器与富文本编辑器优缺点比较

    其实对于用户来说,Markdown编辑器和富文本编辑器的作用是一样的,功能上也没有什么区别,在Markdown诞生之前大家都是用富文本编辑器的,也没见什么功能不能实现的,而两者主要区别在于他们的使用方 ...

  6. markdown编辑器与富文本编辑器优缺点比较,哪个更好用

     其实对于用户来说,Markdown编辑器和富文本编辑器的作用是一样的,功能上也没有什么区别,在Markdown诞生之前大家都是用富文本编辑器的,也没见什么功能不能实现的,而两者主要区别在于他们的使用 ...

  7. starops 云效运维 文档_阿里云 SAE 携手云效助力「石家庄掌讯」持续交付、降本提效...

    背景 石家庄掌讯信息技术有限公司创立于2009年,是一家提供企业信息化咨询.创新型软件产品.电商代运营服务,标准化管理.快速发展的高新技术企业.当前公司正处于企业互联网市场突破转型重要阶段,希望将更多 ...

  8. 特别好用的前端html富文本编辑器wangEditor个人使用案例

    当前为jQuery版本demo,详细Vue组件版本请点此链接:特别好用的Vue富文本编辑器wangEditor自己使用案例组件,附源码,直接使用_膨胀的菜盒的博客-CSDN博客 因公司项目后台管理平台 ...

  9. fastnest怎么一键排版_富文本编辑器的一键排版功能

    在做CMS系统的时候,用户常常会从word粘贴一些东西到编辑器中,早起的富文本编辑器也都提供了去除word格式的功能(尽管有时候比较难用),甚至有时候用户要求打开一个本地的word文件的时候系统能够直 ...

最新文章

  1. raid0+磁盘加密
  2. Java对象的访问定位
  3. 编译安装_Unbound编译安装
  4. shell脚本常用参数与格式
  5. Redis高可用方案-公私混合云
  6. “景驰科技杯”2018年华南理工大学程序设计竞赛 A. 欧洲爆破(思维+期望+状压DP)...
  7. 产品经理之深度学习促进产品之分类(三)
  8. C/C++中的位运算
  9. VISIO画立体图——VISIO画图技巧
  10. 数字图像处理(MATLAB)(第三版) 冈萨雷斯 中的matlab附录代码工具箱
  11. linux 将ext2变成ext4文件系统
  12. 微信小程序发布流程(上传审核)
  13. 视频无法播放,视频打不开怎么办?可用这款视频修复工具快速修复
  14. python与sql的区别_2020年入门数据分析选择Python还是SQL?七个常用操作对比!
  15. 红帽linux内核修复,红帽Linux 7和CentOS 7的新Linux内核更新修复两个错误
  16. 序列的运算、操作、函数/方法
  17. thinkphp5实现文件下载
  18. compareTo()和compare()的详解和区别
  19. threejs 辉光的使用 在vue
  20. 论突变为零(不定更新)

热门文章

  1. C语言:编译时指定头文件路径
  2. Centos hydra安装
  3. USACO Monthly Expense
  4. RTOS中的消息队列的原理以及应用
  5. Javascript语义分析器,教你如何实现Jquery库
  6. 分享几个好看的Bootstrap后台管理响应式模板
  7. fish shell一个专为90后设计的命令行shell
  8. matlab计算公式中的累加,Matlab系列教程之数值计算_求和(积)_求累加(积)
  9. Matlab:多项式的四则运算
  10. 影院活动管理系统--项目设计阶段.