高级特性案例:Suggestion / Table / NodeView
1. Suggestion:输入法、光标和异步数据的交叉地带
核心文件:packages/suggestion/src/suggestion.ts
1.1 插件状态机
Suggestion 插件内部维护:
activerangequery/textdecorationIdcomposing
状态由 transaction apply 驱动,view update 决定 onStart/onUpdate/onExit 触发时机。
1.2 输入法兼容(IME)
代码中显式处理 composing 场景,并在注释中引用历史问题(issue #1449)。
本质是:IME 组合输入时 selection 与文本更新时序不同于英文输入。
1.3 协同友好的 shouldShow
v3.15 增加 shouldShow(并传入 transaction),可过滤“远端事务触发本地弹层”的误触发。
证据:
- Changelog v3.15.1
- 单测:
packages/suggestion/src/__tests__/suggestion.test.ts
1.4 安全退出机制
提供 exitSuggestion(view, pluginKey),通过 metadata-only transaction 退出,不改文档。
这是避免 decoration 映射错乱的关键策略。
源码片段(packages/suggestion/src/suggestion.ts):
ts
export function exitSuggestion(view: EditorView, pluginKeyRef: PluginKey = SuggestionPluginKey) {
const tr = view.state.tr.setMeta(pluginKeyRef, { exit: true })
view.dispatch(tr)
}2. Table:复用 PM tables,同时暴露更友好的业务 API
核心文件:packages/extension-table/src/table/table.ts
2.1 设计策略
- 底层命令基本代理
@tiptap/pm/tables(addRow/deleteColumn/mergeCells...)。 - 上层补充:
insertTablemergeOrSplitsetCellSelection- markdown parse/render 适配
2.2 编辑态与只读态表现差异
resizable && editor.isEditable时才注入columnResizing。- 新增
renderWrapper选项,控制只读模式是否也保留 wrapper(见 changelog v3.9.1)。
2.3 键盘行为工程化
Tab 在末单元格时自动新增行并跳转。
这类“接近产品期望”的行为不是 PM 原生提供,而是扩展层封装出的高阶语义。
3. NodeView:可视化自定义的桥
3.1 Core NodeView 基类
位置:packages/core/src/NodeView.ts
提供:
- drag handle 处理
stopEvent/ignoreMutation细粒度控制- iOS/Android 输入边界兼容逻辑
updateAttributes/deleteNode公共能力
3.2 ReactNodeViewRenderer 适配层
位置:packages/react/src/ReactNodeViewRenderer.tsx
关键点:
- 用 ReactRenderer 挂载组件并回填
contentDOMElement。 - selection 更新走
requestAnimationFrame节流。 - 支持
update回调做自定义 diff,减少不必要 re-render。 - 强约束
NodeViewWrapper(避免结构不合法)。
3.3 ReactRenderer 的生命周期细节
位置:packages/react/src/ReactRenderer.tsx
- editor 已初始化时
flushSync保证与 PM 渲染节奏一致。 - 未初始化用
queueMicrotask延后,且带destroyed守卫。 - destroy 时移除 portal 与 DOM 节点,避免泄漏。
这是把“外部渲染器”嵌入 PM NodeView 生命周期的经典实现。
4. 可讲的统一结论
Suggestion、Table、NodeView 三类高级特性共同体现:
- 业务语义封装在扩展层,不污染内核。
- 复杂时序通过 transaction + plugin state 管理。
- 对浏览器兼容与框架生命周期边界有大量工程化兜底。