跳到正文
QuireKit MSFE
开发者文档
v0.0.0 · 实验版本

投影层与组件生命周期

正确实现内联装饰、跨行预览、状态依赖和测量。

源码、描述和 DOM 三层

源码 codec 读取语义;投影层根据已接受输入返回声明式描述;renderer 才创建 DOM 或框架组件。不能在 build() 中创建图表、发网络请求或调用编辑器提交事务。

输出典型用途限制
mark高亮、拼写标记装饰范围使用规范源码坐标
line原生段落/容器行样式锚定行起点
widget控件、提示组件不可接管正文输入
replace + text源码中立的字面呈现文本不会按 HTML 解释
replace + view图表和复杂预览跨行结构需要 block 层

inline 和 block 不能混用

placement: 'inline' 在有界可见范围中工作,不应返回跨行结构性替换。placement: 'block' 的结果在 CodeMirror 计算视口之前提供,可参与多行高度计算。

普通节点源码 renderer 的 placement: 'replace' | 'before' 是行内视图入口,不能把它等同于任意整块编辑容器。图片可用特定的 measureHeight 协议;大型围栏预览使用 block 层,见 图表完整示例。

声明依赖和稳定身份

ts

层的 key 全局唯一;视图条目的 key 在层内稳定。不要在 build() 中创建新的 renderer 函数,避免每次状态改变都卸载组件。示例保留 renderer 模块级常量,但实例资源仍由组件作用域管理。

读取 input.selection 的构建器必须设置 selection: true;读取状态槽时把槽列入 states。普通源码显露已由运行时处理,不需要因此让每次光标移动都重建全部图表。

modes 默认 live / locked。suppressDuringComposition 默认 true:Markdown 编辑中,仅与输入法选区相交的描述暂时回退源码;替换描述以 revealRange 判断完整语义范围,行描述以该行判断。其他块保留呈现与组件身份。隐藏标记的文档编辑保持投影,原生正文仍由 CodeMirror 接受组合输入。

组合输入开始/结束只重新物化已有描述,不要求重建整篇层。预览仍在页面上时,“编辑源码”等操作也必须检查 readSourcePresentation(editor.state).composing,避免抢走输入法焦点。不要为视觉稳定取消输入保护。冲突替换可能回退源码,可查看 editor.work.projectionConflicts 定位所有权争用。

普通输入时只更新受影响块

当输出只依赖当前语法块时,可以声明 incremental: true。块级层会收到变更后的完整语法块,未受影响的描述映射到新源码位置并保留组件身份;内联层仍按可见范围查询,并复用映射后的组件身份。省略该选项时继续完整构建,以兼容依赖全文的插件。

启用前需满足以下约定:

  • 使用 input.query(name) 或本次范围内的源码,输出及 revealRange 属于所查询的块。不要在每次构建中扫描全文、给全文编号或生成跨块目录。
  • 引用链接依赖其他位置的定义时声明 references: true,新增、删除或修改定义都会重新构建该层。其他全局依赖仍应使用保守构建。
  • selection 和 states 仍须声明;权限、模式或这些依赖变化时,运行时会重新评估完整范围。
  • key 在一次构建中标识唯一源码位置,renderer 保持稳定。组件身份由运行时沿源码变化映射,整块覆盖或删除会终止旧身份。

如果组件属性带有源码位置,用 componentView 的第三个参数显式映射。下面是放在 input.query(name).matches.map(...) 中的视图片段,renderPreview 是模块级 renderer:

ts

映射函数只返回新属性,不创建 DOM、请求资源或提交事务。renderer 的 update(next) 必须保存最新属性,点击操作不能闭包捕获第一次挂载的位置;预览内容没有变化时,应保留现有图表实例。这样在前方输入后,即使预览稍后才滚入视口,其“编辑源码”操作也会定位到正确块。

局部更新不等于只解析光标所在行。修改代码围栏、列表/容器边界、表格分隔行或引用定义,可能合法地影响前后块;运行时以解析器确认的结构变化扩大更新范围。可对比 editor.work 中的 syntaxDiffBuilds、syntaxDiffChunks、syntaxDiffComparisons、layerVisitedNodes 与 blockBuilds,并验证 DOM/资源是否复用。当前仍需映射既有描述并生成装饰,不保证所有输入都是恒定成本。

ComponentView 与 NodeView

协议输入场景
NodeViewnodeView.attrs、updateAttributes、getRange 等与源码字段绑定的控件
ComponentView自定义 view 属性搜索、折叠按钮、图表等通用组件

Vue 使用 VueNodeViewRenderer / VueComponentViewRenderer,React 使用对应适配器。两类视图都需要原来的 Provider 树中的 Host;不要给每个节点创建独立应用。

原生 ComponentViewRenderer<Props> 取得 dom、props、host、scope 和 requestMeasure,返回 update、destroy,可选 ignoreEvent。ignoreEvent 默认 true;只有明确希望 CodeMirror 消费的事件才返回 false。

高度与卸载

挂载容器归运行时所有。renderer 管理自己的子 DOM、监听器、观察器和第三方实例,不改写 .cm-content。异步图片或图表尺寸变化后调用 requestMeasure(),它合并测量请求。

视图更新应复用资源;源码或关键渲染参数变化时取消旧请求。destroy() 移除框架入口,scope 结束时释放资源。二者都要能正确处理未完成的加载。完整模式见 资源生命周期。

需要验证的交互

检查鼠标拖选经过预览、键盘进入源码、搜索命中、只读复制、IME、滚动卸载、快速更新及换文档。先比较工作计数变化和资源存活数量,再检查最终画面;光标移动不应触发全文解析或所有图表重新初始化。协议见 渲染 API。

MSFE · QuireKitMarkdown 源码始终属于你。