Skip to content
目录

slate-react 事件管线与输入法兼容

1. 先给结论

Editable 的核心职责不是渲染,而是“把不稳定的浏览器输入事件,转成稳定的 Slate 操作序列”。

尤其 IME(中文/日文/韩文)场景,Slate 做了大量防抖、延迟、兜底与平台分流。


2. 事件主链路(简化版)

  1. 原生 beforeinput(不是 React polyfill)
  2. 解析 inputType(insert/delete/composition/drop/paste)
  3. 必要时先同步选区(toSlateRange -> Transforms.select
  4. 决定 native 还是 Slate 接管(event.preventDefault 与否)
  5. 调用 Editor.insertText/delete* 等命令
  6. 结束后尝试恢复用户真实选区

源码依据:

  • packages/slate-react/src/components/editable.tsx
  • 明确注释:React 的 beforeinput 是“leaky polyfill”,所以要绑定原生事件

源码注释(节选):

ts
// Listen on the native `beforeinput` event to get real "Level 2" events.
// This is required because React's `beforeinput` is fake...
node.addEventListener('beforeinput', onDOMBeforeInput)

3. 为什么 IME 难

IME 期间常见特性:

  • beforeinput 不一定可取消(Android 更明显)
  • composition 事件顺序在不同浏览器不一致
  • DOM 可能先被浏览器改,再轮到框架状态更新

Slate 的应对策略:

  • 维护 isComposing 状态
  • composition change 类型先放行,不急于干预
  • Android 走专门 input manager 分支

源码注释(节选):

ts
// COMPAT: BeforeInput events aren't cancelable on android,
// so we have to handle them differently using the android input manager.
if (androidInputManagerRef.current) {
  return androidInputManagerRef.current.handleDOMBeforeInput(event)
}

相关实现:

  • use-composing.ts
  • hooks/android-input-manager/*
  • editable.tsxonCompositionStart/Update/End + onDOMBeforeInput

4. Android 专用输入管理器

文件:packages/slate-react/src/hooks/android-input-manager/android-input-manager.ts

这是一个非常工程化的补丁层:

  • 监听 MutationObserver
  • 结合 selection 状态决定何时 flush
  • 对部分 inputType 做特判
  • 避免“看起来输入成功,但 Slate 状态和 DOM 不一致”

对应 issue:


5. 兼容策略中的现实权衡

Editable 中经常出现:

  • “某浏览器可用 native 插入更快,但某场景会重复插入”
  • “理论上应全部 preventDefault,但某些输入法不可取消”
  • “应该马上同步选区,但过早会导致跳光标”

所以 Slate 采用了条件式 native fallback延迟回补并存的策略。

这也是为什么富文本输入层很难“优雅重写”。


6. 与 React 状态同步

packages/slate-react/src/components/slate.tsx 中:

  • 初始化时写入 editor.children = initialValue
  • onContextChange 里根据 operation 类型区分触发:
    • onSelectionChange
    • onValueChange
    • onChange

这保证了“选择变化”和“内容变化”可分流订阅。


7. 答法要点(聚焦工程认知)

Slate React 层本质是输入事件编排器。
对 IME 的处理不是单个 bugfix,而是完整策略:原生 beforeinput、composition 状态机、Android 专用 manager、选择恢复机制。
这类代码的难点在于跨浏览器行为一致性,不是 API 设计本身。

MIT License.