js给文本中数字加颜色_一窥 Draft.js 受控渲染机制

估计很多同学都知道 Draft.js 是基于受控的 (controlled) contenteditable 实现的,也知道 Draft.js 的 editor state 中除了 content state,还有 selection state,undo/redo stack 等。但具体 Draft.js 如何将这些 state 渲染出来,估计多数同学就不了解了。毕竟不了解也能用起来。

最近 de - 一个流程编辑器的万年 - bug,心里骂了一万遍垃圾 Draft.js,都做好重构准备了,还是耐下性子打日志加断点,一点一点挖,结果发现还真不是 Draft.js 的锅。在此过程中也大概了解 Draft.js 如何把 editor state 渲染到 DOM,本着文章写一篇是一篇的精神,和同学们交流一下(神奇的是,全网好像都没有啥文章讲这个)。

本文不是详细分析,仅介绍大概结构。毕竟,我也没详细看。

四层结构

Draft.js 至上而下可以分为 EditorEditorContentsEditorBlockEditorLeaf 这四层。其中大家熟悉的应该有 EditorEditorBlock 这两层。

EditorContents 是比较轻的一层, 基本就是循环渲染所有的 block。

EditorBlock 是我们在写自定义 block 的时候可能需要加的东西,(大概是)用于告诉 Draft.js 哪里是可编辑区域。

不过我前面提到的万年 bug 也就是这一层引起的:为了避免不必要的渲染(比如移动了光标后,新 state 和当前 DOM 自然是同步的,无需重新渲染),这一层有一个长长的 shouldComponentUpdate。然而我们的自定义 block 使用了 mobx,用到且会提交 editor state,那么由于上层的 shouldComponentUpdate,自定义 block 需要重新渲染时,就会把老 editor state 抛出去导致状态回滚,在我们遇到的问题中具体表现就是光标被锁死。

EditorLeaf 则是单一样式的一坨内容 ,这个组件也是处理 selection state 的地方。

状态渲染

关于状态渲染,其实重点基本上就是 selection state 的处理了,毕竟 React 渲染个 DOM 一点也不令人感动。

如果你写过 Draft.js 的插件,那么很可能对 forceSelectionacceptSelection 有印象。为啥有时两个都好用,有时又必须 forceSelection?其实原因就是前文提到的 EditorContentsshouldComponentUpdate 中,将这一点作为了判断是否需要重新渲染的条件之一。如果一些特定的状态没有发生改变,又不是“must force selection”,那么 DraftContents 会直接跳过重渲染,也就导致光标没有和 editor state 同步。

而 Draft.js 中很巧的一点就在于,通过 EditorLeaf,将 selection state 分而治之。核心代码就几行(雾):

componentDidUpdate(): void {
  this._setSelection();
}

componentDidMount(): void {
  this._setSelection();
}

举例来说,假设我们有一段文本,分成了三个 leaf:“客官,买个葫芦?”。简化成字符串的数组可以这样表示:

['客官,', '买个葫芦', '?']
为什么要买葫芦?不是,“买个葫芦”其实是 Makeflow 的中文名。

假设我的 selection state 是从“官”选到“?”:

  • 第一个 DraftLeaf 会发现选择是从自己这里的“官”开始,并未在自己这里结束,于是选择“官,”。
  • 第二个 DraftLeaf 会发现选则从自己之前到了自己之后,于是扩展选择了整个“买个葫芦”。
  • 第三个 DraftLeaf 发现选择是从自己之前开始,并在自己这里结束,于是扩展选择了“?”。

这个乍一看扑朔迷离的 selection state 就这么容易地被搞定了。

一点评论

Draft.js 刚出来的是我是有种惊为天人的感觉,后续也使用过 Slate.js,再后来又换回了 Draft.js,现在我是又想换到 Slate.js,真是造化弄人。目前维护情况来讲,Draft.js 好像是不大行了,0.11 版憋了多年终于憋了出来,但稍微一用就发现了核心 DOM mutation 逻辑的大问题,赶紧又退回了 0.10。

Draft.js 使用 contenteditable 是为了复用浏览器自身提供的各种超能力,然而尽管想法包括实现讨巧,依然摆脱不了各种兼容性问题,还是希望能有富文本编辑器框架能使用类似于 Monaco 编辑器的机制(textarea 做文本输入)。


Makeflow (makeflow.com) 是以流程为核心的项目管理工具,让研发团队能更容易地落地和改善工作流,提升任务流转体验及效率。如果你正为了研发流程停留在口头讨论、无法落实而烦恼,Makeflow 或许是一个可以尝试的选项。如果你认为 Makeflow 缺少了某些必要的特性,或者有任何建议反馈,可以通过 GitHub、语雀或页面客服与我们建立连接。

题图 by Hal Gatewood on Unsplash

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SELECT DISTINCT c.ID AS id, c.NAME AS contName, c.CONTRACT_NO AS contractNo, c.INSTANCE_ID AS instanceId, c.UNDERTAKE_DEPT_ID AS remindDeptId, c.UNDERTAKE_DEPT_NAME AS sendDeptName, c.CREATE_USER_ID, c.CREATE_USER_NAME AS contractOpteraterName, c.PLAN_STATE AS planState, c.PLAN_STATE_NAME AS planStateName, aw.INSTANCE_ID AS inId, aw.CREATE_TIME AS sendTime FROM ( SELECT c.* FROM ( SELECT c.* FROM ( SELECT c.ORIGINAL_CONTRACT_ID, MAX(CREATE_TIME) CREATE_TIME FROM CONTRACT_DRAFT.C_CONTRACT_INFO c WHERE c.ORIGINAL_CONTRACT_ID IS NOT NULL AND c.ORIGINAL_CONTRACT_ID != '' GROUP BY c.ORIGINAL_CONTRACT_ID ) t LEFT JOIN CONTRACT_DRAFT.C_CONTRACT_INFO c ON t.ORIGINAL_CONTRACT_ID = c.ORIGINAL_CONTRACT_ID AND t.CREATE_TIME = c.CREATE_TIME UNION ALL SELECT c.* FROM CONTRACT_DRAFT.C_CONTRACT_INFO c WHERE ( c.ORIGINAL_CONTRACT_ID IS NULL OR c.ORIGINAL_CONTRACT_ID = '' ) AND c.ID NOT IN ( SELECT c.ORIGINAL_CONTRACT_ID FROM CONTRACT_DRAFT.C_CONTRACT_INFO c WHERE c.ORIGINAL_CONTRACT_ID IS NOT NULL AND c.ORIGINAL_CONTRACT_ID != '')) c WHERE c.deleted_flag = 0 AND c.BELONG = 1 AND sysdate > c.end_date AND c.plan_state IN (4100, 4110, 4120, 4200, 4210, 4220, 5100, 5110, 5120) ) c INNER JOIN (SELECT INSTANCE_ID,create_time,state FROM CONTRACT_DRAFT.C_ACTIVITY_WORKITEMS WHERE state = 'Waiting') aw ON c.INSTANCE_ID = aw.INSTANCE_ID LEFT JOIN (SELECT deleted_flag,CONT_ID FROM CONTRACT_DRAFT.C_GET_PAY_PLAN WHERE deleted_flag = 0 ) g ON c.ID = g.CONT_ID LEFT JOIN CONTRACT_DRAFT.C_OUR_ENTITY_INFO oe ON c.OUR_ENTITY_ID = oe.ID AND oe.DELETED_FLAG = 0 ORDER BY aw.CREATE_TIME DESC 优化
07-08
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值