ProseMirror 系列收官:从这套代码里能拿走的设计
60 篇的收官篇:不可变文档与显式映射的更新模型、核心薄扩展厚的边界、插件系统的能力分层,每个设计论断都指回具体篇目,附全系列地图与完整索引。
共 60 篇文章
60 篇的收官篇:不可变文档与显式映射的更新模型、核心薄扩展厚的边界、插件系统的能力分层,每个设计论断都指回具体篇目,附全系列地图与完整索引。
从文档模型、更新模型、合法性校验、扩展机制四层对比 ProseMirror 与 Draft.js、Slate、Quill 的结构差异,ProseMirror 侧逐条对应本系列前 58 篇拆过的机制。
meta 仓库 bin/pm.js 的命令面、buildhelper 的构建测试约定,以及 changelog 驱动的 release 管线如何统一管理 20 个仓库的版本与依赖。
prosemirror-test-builder 的两个源文件:block/mark builder 的生成与默认 attrs、字符串里 标签的位置记账、flatten 的嵌套 tag 平移,以及它在 prosemirror-transform 测试里同时充当操作参数和映射断言锚点的用法。
prosemirror-example-setup 的装配逻辑:七个插件按固定顺序拼装,顺序决定按键与输入的分发优先级,三个 build 函数按 schema 按需生成绑定,以及它离生产级编辑器差什么。
prosemirror-tables 的 columnresizing 插件与 TableView:widget 装饰生成拖拽手柄,mousemove/mousedown 的事件链,拖拽期间只改 DOM 的实时反馈,松手时 colwidth attr 一次写回,以及 handleWidth 等配置与插件拼装顺序。
prosemirror-tables 的命令层:selectedRect 把选区翻译成 TableRect,addColumn/addRow 在格网定位后改 span 或插新格,mergeCells/splitCell 重算 span 与 colwidth,goToNextCell 按文档顺序导航。
prosemirror-tables 的 CellSelection:用 anchorCell/headCell 两个位置表达矩形选区,isRowSelection/isColSelection 的判定与扩展,选区可视化的装饰方案,以及跨表格时的边界处理。
prosemirror-tables 的地基:tableNodes 工厂生成的四个节点 spec、colspan/rowspan/colwidth 三个 attr,TableMap 的扁平格网与 problems 诊断,以及 findTable、cellAround 等定位函数。
prosemirror-search 的两个文件 :SearchQuery 的文本块扫描与正则匹配、range 随文档变化的 Mapping 维护、getReplacements 的 skip/copy 区间,以及命令与装饰的组合。
prosemirror-markdown 的双向转换机制:MarkdownSerializer 的渲染表与状态机,markdown-it token 流接入 MarkdownParseState 的栈,以及与 DOM 通路的对照。
prosemirror-changeset 的 span 表示:Change 的 A/B 双坐标、Change.merge 在中间坐标系上的归并、addSteps 之后用 Myers diff 最小化,以及 simplifyChanges 的按词化简。
prosemirror-collab 的 API 面与使用方分工:unconfirmed 的三类变动、history 在 rebase 后的 Branch.rebased 重建、preserveItems 的两处作用,以及官网 demo 里收发循环的完整时序。
prosemirror-collab 的集中式权威模型:version 与 unconfirmed 两个字段怎么记账,远端 step 到达时本地未确认 step 如何经 rebaseSteps 撤销、追赶、映射重做。
prosemirror-menu 的 MenuElement 协议、MenuItem 的 run/select/enable/active 四件套、Dropdown 组合、menubar 插件的 floating 模式,以及这套 UI 层为什么留在核心之外。
dropcursor 在 dragover 事件里算出落点,用一个绝对定位的 div 画指示线;块级位置画横线、行内位置画竖线,内部拖拽经过 dropPoint 的 schema 校验。
两个块级节点之间的文档位置没有文本容器,TextSelection 与 NodeSelection 都落不进去;gapcursor 用一种选区类型加一个 widget 假光标补上这类缝隙。
prosemirror-schema-list 全文件解读:list_item 内容表达式的设计,wrapInList、splitListItem、liftListItem、sinkListItem 四条命令的逐步推导,以及列表为什么是全库边界情况最密集的代码。
prosemirror-schema-basic 全文件解读:九个节点与四个 mark 的 spec 逐一定义,marks 排除规则的三层实例,以及列表节点为什么留给 schema-list。
prosemirror-inputrules 全文件解读:InputRule 的 match 与 handler、正则 $ 结尾匹配的语义,handleTextInput 挂载点,wrappingInputRule 与 textblockTypeInputRule 两个构造器,以及 undoInputRule 怎么把规则效果和输入文本分开撤销。
prosemirror-history 全文件解读:Branch/Item 双栈结构,逆步与选区书签组成的事件,时间阈值加位置相邻的分组启发式,以及协作 rebase 对历史栈的重写。
prosemirror-commands 全文件解读:Command 的 (state, dispatch, view) 签名与 dry-run 惯例,chainCommands 的短路语义,baseKeymap 逐条拆解,以及内置命令对 structure.ts 判断函数的消费方式。
prosemirror-keymap 全文件解读:handleKeyDown 挂载方式、Mod-/Shift- 键名规格的归一化、一次按键最多三次查表的兜底逻辑,以及多 keymap 组合时的顺序语义。
只用 model、transform、state、view 四个核心包手写一个支持输入、删除、加粗的最小编辑器,每个行为对应到前面各篇讲的机制,并用一张 API 表收束核心层。
browser.ts 的 UA 嗅探与能力探测、补丁在 view 各模块的具体分布、dom.ts 的 DOM 工具函数族,以及编辑器为什么需要集中的一层浏览器差异补丁。
posAtCoords 与 coordsAtPos 的双向换算实现:相邻字符矩形压扁的定位策略、caretFromPoint 的逐层下钻与浏览器补丁、endOfTextblock 用 Selection.modify 实测边界,以及 scrollRectIntoView 的逐层滚动。
serializeForClipboard 与 parseFromClipboard 的实现:data-pm-slice 元数据的往返、text/html 与 text/plain 的取舍、外来 HTML 的 sibling 归一与 doPaste 的插入逻辑。
prosemirror-view 的 Decoration 体系:widget/inline/node 三种类型、DecorationSet 的局部数组加子树三元组结构、map 跟随文档的映射规则,以及 viewDecorations 的生成与渲染消费。
NodeView 接口的七个委托方法、CustomNodeViewDesc 接入 ViewDesc 树的方式、contentDOM 的内容托管约定,以及 MarkView 与 NodeView 的差异。
compositionstart/update/end 在 prosemirror-view 里的完整处理:组合期间的 DOM 写入保护、endComposition 的读回收尾、compositionID 的撤销分组,以及移动端和 Safari 的补丁分布。
selectionToDOM 怎么把 state 选区写进 DOM,selectionchange 怎么被读回 state,syncNodeSelection 与一批边界工具函数如何兜住浏览器的选区怪癖。
事件注册表 handlers 与 editHandlers 的分工、someProp 的统一入口、keydown 的完整处理路径、captureKeys 的兜底过滤,以及 beforeinput 的降级补丁。
浏览器直接改 DOM 之后,MutationObserver 怎么攒记录、flush 怎么聚合成文档区间,readDOMChange 又如何用 diff 把变化反推成一次 transaction。
ViewDesc 的增量更新机制:matchesNode/matchesMark/matchesHack 的复用判定、dirty 四级标记、iterDeco 的装饰注入位置,以及文本节点的就地更新。
ViewDesc 描述树的静态结构:类族分工、children 与文档树的对应、docViewDesc 的构建路径,以及 dom、contentDOM、nodeDOM 三个引用的关系。
EditorView 总览:构造顺序、props 与 DirectEditorProps、dispatchTransaction 惯例、updateState 的完整路径、pluginViews 的挂载与 destroy 的清理面。
用三个完整可运行的小插件(字符统计、只读开关、选区上报)把 prosemirror-state 插件系统的 StateField、filterTransaction 与 view 规格串起来用一遍。
插件规格的另外三个接口:filterTransaction 的否决权、appendTransaction 的链式修正与收敛约定、view 与 props 如何把插件接到 EditorView 上。
prosemirror-state 的插件系统上半部分:PluginSpec、Plugin、PluginKey 三件套的分工,StateField 的 init/apply 签名与纯函数约束,插件顺序对字段初始化的影响,以及 key.getState 的取数路径。
Transaction 对 Transform 的四项扩展:惰性映射的选区、storedMarks 的失效规则、meta 的插件通信惯例,以及 scrollIntoView 与 time 字段的消费方。
prosemirror-state 的选区体系:TextSelection、NodeSelection、AllSelection 的 anchor/head 语义,SelectionBookmark 的 map 与恢复,replace 的落点计算,以及 $cursor 与 inline 位置的边界处理。
EditorState 的字段分层、create 的插件初始化流程、applyTransaction 的两阶段应用过程,以及 reconfigure 换插件时的字段保留规则。
Transform 类如何用 steps/docs/mapping 三个数组组织一次修改,replace/delete/insert 便捷方法的分层,以 及 addMark/removeMark 的范围处理。
structure.ts 里 canSplit、canJoin、liftTarget、findWrapping 四个纯判断函数的校验逻辑,以及 prosemirror-commands 对它们的消费方式。
Mapping 类的 maps/mirror 双数组、多步链式映射的删除信息累计、镜像步跳转与 recover 找回机制,以及它给 rebase 打下的地基。
StepMap 的区间段编码、map 的 assoc 方向偏好、MapResult 的删除标记位,以及 recover 值的设计与用途。
ReplaceStep 与 ReplaceAroundStep 的字段、映射与合并规则,以及 Fitter 如何用 frontier、placed、unplaced 三块状态为 slice 寻找闭合节点序列。
Step 抽象基类的接口划分、StepResult 的失败语义、AddMarkStep/RemoveMarkStep 与 AttrStep 的实现,以及可逆性对 undo 的意义。
model 阶段收官:Node 上遍历、校验、定位三族辅助方法的实现与边界行为,以及 model 层 API 的心智模型总结。
diff.ts 两个函数如何从两端扫描定位 差异区间:引用相等与 sameMarkup 的分层判定、compareDeep 的 attrs 深比较,以及 view 层 readDOMChange 对 diff 结果的消费。
parseDOM 规则的 priority 排序与 tag/style 匹配过程,ParseContext 的节点栈状态机(findPlace、findWrapping、closeExtra、sync),context 上下文限定与空白符处理策略。
DOMOutputSpec 三种形态与 0 号洞的约束,renderSpec 的递归与 XSS 防护,serializeFragment 用 active 栈合并相邻 mark 的过程,以及纯文本输出走 leafText 的约定。
Slice 用 openStart 与 openEnd 记录切片两端被切开的层数,replace 用两路与三路合并把切片接回文档,沿途用 close 做 schema 校验。
pos 的扁平编号约定、ResolvedPos 的 path 结构与 depth/parent/index/offset 字段族、start/end/before/after 的边界语义,以及 resolve 的成本与缓存策略。
NodeSpec 各字段的含义,content expression 从字符串编译成 DFA 的过程,createChecked 的校验路径,以及 Schema 构造器在 compile 阶段做的全部工作。
Mark 与 Node 的不对称设计:Mark 不进文档树,以排序数组挂在节点上。addToSet、removeFromSet、sameSet 的实现,excludes 的单向互斥语义,以及为什么粗体斜体不做成嵌套节点。
Node 的四个字段与不可变约定,Fragment 的 size 缓存与位置换算,nodeSize 与 childCount 两套计数,以及叶节点和文本节点的特例。
用 prosemirror-example-setup 几十行代码搭出编辑器,在控制台里看文档的 JSON 结构和一次输入产生的 Transaction,为后续读源码建立直观模型。
ProseMirror 工作区根目录布局与 22 个包的 dependencies 逐层梳理:核心四层单向依赖、扩展只朝下依赖核心、拆成多包的工程理由。
从 contenteditable 的浏览器差异与 HTML 即模型的混乱讲起,说明「文档即数据」的思路和 ProseMirror 的四条设计原则,并交代整个源码分析系列的读法与规划。