Recent Posts
Arc 浏览器的个人体验
Published:一人游|国内24天记录
Published:一个表格组件
Published:Eiinu 的博客 3.0
Published:2019-2024,新的开始
Recent Memos
4. 当 Quill 遇上 Vue Reactivity
Published:Quill 编辑器的实例很重要,对内容的各种操作、事件监听都需要直接调用实例上的方法,它是一个层级很深、非常复杂的 js 对象(下面都以 quill 称)。
当在 Vue3 项目中使用 quill 时,可能随手就将它放进了 ref 里,默认会被深层代理。这里就会出大问题。
如果直接通过代理的方式访问 quill 上的任何方法时,浏览器可能会报错(找不到 blot 上的 offset),不仅操作不会生效,而且后续会一直报错,出现键盘操作无响应、注册的事件不触发、显示内容与 delta 数据不一致等等各种问题,几乎就是彻底崩溃了。
猜测原因是 Vue Proxy 数据代理机制会对这种第三方的非响应式的对象产生影响。
解决办法就是不对它进行代理:
// 1、不使用 vue ref let quill = null onMounted(() => { quill = new Quill({}) }) // 2、用 markRaw 标记 quill 实例 const quill = ref(null) onMounted(() => { quill.value = markRaw(new Quill({})) })
Vue 文档中有关于
markRaw
的详细介绍,里面专门提到了「有些值不应该是响应式的,例如复杂的第三方类实例或 Vue 组件对象」,可参考:markRaw。3. 浏览器选区之 hanging Range
Published:浏览器默认有这样一个行为:当用户双击一段文本时,会选中单词(中文有分词处理),当三击时,会选中整段文本。
这个「选中整段文本」的行为所产生的浏览器选区 Selection 对象上,
extendNode
、focusNode
是当前块级文本的下一行的节点。而如果用户通过手动拖拽行为,从段落开头框选到段落结尾,这时产生的选区对象上,上述的两个字段是当前块级文本的末尾节点。
对比这两种操作,用户看到的选区是一样的,实际生成的选区对象却是不同的,从编辑器框架的角度看,前者的 range 似乎比后者多了 1。
这就会造成很多问题,如果在此时进行插入带格式文本的操作,就会有意料之外的效果。
那么如何解决这个问题?
- Quill 没有处理这种情况,两种操作下获取到选区的长度相差 1。所以我遇到了 bug,也才有了本文的记录。
- ProseMirror 实现了三次点击的行为监听,在其中拦截了,参考 ProseMirror 源码。
- Slate 将这种选区称为「hanging range」,并对它进行一次
unhang
的格式化处理,在其中重新计算了选区结尾节点,参考 Slate 源码。关于它,也有一些比较有趣的社区讨论。