ProseMirror View 模块
prosemirror-view 模块主要用于将编辑器状态对象 state 展示在页面(渲染为 DOM),并处理用户与编辑器的交互事件。
提示
如果使用该模块,请确保在页面中引入了 style/prosemirror.css 样式表,以提供必要的外观设置
EditorView 类
该类用于管理页面上的一个 editable DOM(并让编辑器状态对象 state 与这个 DOM 的更新保持同步)
通过方法 new EditorView(place, props) 进行实例化,得到一个 view 对象,以下称作「编辑器视图对象」,它对(用于表示/反映编辑器状态的)editable DOM 进行管理,相关参数的具体说明如下:
编辑器状态与 editable DOM 保持同步
编辑器视图对象 view 将 editable DOM 的变动同步到编辑器状态 state 上,这样就可以确保 editable DOM 实时反映了编辑器当前的状态
其流程大致如下:
- 用户与 DOM 进行交互
- 利用浏览器原生的引擎对 editable DOM 进行修改
- 编辑器视图对象 view 基于 DOM 的变动修改编辑器状态,以实现编辑器与页面的 DOM 相同步
所以一般先利用浏览器原始的编辑器行为 editing action 响应用户的操作(对 editable DOM 进行修改),然后 ProseMirror 再基于 DOM 的变动分发 transaction 更新编辑器状态(这一步一般是不需要再去改变 DOM,仅更新编辑器状态对象即可;只有遇到特殊的需求而导致不应用浏览器默认的编辑行为时,才需要再去改变 DOM,例如 transaction 被取消,则需要将 DOM 恢复为用户操作之前的样子
对于选区也是利用浏览器原生的引擎先进行更改,然后 ProseMirror 再分发 transaction 更新编辑器状态,让(存储在编辑器状态里)selection 与 editable DOM 的选区实现同步
虽然 ProseMirror 支持通过监听(editable 编辑相关的)事件 ❓ 对操作进行劫持(取消浏览器的默认行为),以实现定制化的响应,但是 ProseMirror 一般使用浏览器的默认行为来响应用户的操作,因为原生引擎就已经可以很好地处理许多场景(而无需编写复杂的代码)
例如在两个较长的段落中间夹着一个较短的段落,通过键盘的上下键进行光标导航,光标移到较短的段落时光标会置于末尾,而光标再移到较长的段落时浏览器原生引擎可以准确地恢复光标的水平位置(而如果对按键操作事件进行监听,阻止默认的行为,则会丢失这个功能)

- 第一个参数
place用于指定编辑器会在页面的哪个位置,其值有多种类型,以分别适用不同的场景:- 可以是一个
DOMNode对象 它作为编辑器的容器
ProseMirror 会自动使用方法document.createElement("div")创建一个 editable DOM,作为编辑器的根元素,并将它追加 append 到place参数所指定的 DOMNode 对象里,作为它的子节点(但是保留其原有的子节点) - 可以是一个函数
fn(editor: HTMLElement)提供更灵活的方式将编辑器插入到页面
该函数会接受一个参数editor它是一个 editable HTMLElement 元素(由 ProseMirror 自动使用方法document.createElement("div")创建的),。如果要在创建编辑器视图时执行额外的逻辑操作则可以使用该函数。 - 可以是一个对象
{mount: HTMLElement}
即该对象包含一个属性mount,其值是一个 editable HTMLElement 元素,直接将其作为编辑器的根元素(可以理解为 ProseMirror 接管了这个 DOM 元素作为编辑器,而不再另外创建一个 editablediv元素来作为编辑器的根元素) - 可以是
null,则表示编辑器还没有添加 mounted 到页面上,即该方法所创建的编辑器视图 view 还没有与页面上的任何 editable DOM 相关联
- 可以是一个
- 第二个参数
props是一个配置对象(它需要符合一种 TypeScript interfaceDirectEditorProps),用于设置编辑器视图相关的状态和行为props
props 是单词 properties 的缩写,常见于现代前端框架中,一般是指父组件向子组件传递的数据或属性,ProseMirror 借鉴了这个概念
在这里的参数
props用于对编辑器视图(可以将其类比为 UI 组件)进行配置在现代前端框架中,一般遵循单向数据流的模式向子组件传递的数据,即 props 不能在子组件中直接进行修改(只能将 props 的值作为初始值,赋值给子组件内部逻辑中变量,再对该变量进行修改),只能在使用组件的上下文代码(即父组件)中进行修改
对于这里的参数
props也是一样的,不能在编辑器视图中对该参数对象的属性直接进行修改,只能在传递/配置参数props的上下文代码中对它的属性进行更新。 ⚠️ 但是属性props.state除外,因为编辑器状态对象是 immutable 不能直接通过变更 prop 来修改 state ❓ 可以通过方法editorView.updateState()来更新 state另外可以在实例化插件时,通过配置对象的属性
props(符合 TypeScript interfaceEditorProps)来设置视图的 props 参数(这些插件在编辑器视图实例化时进行注册)ts// 该插件的作用是基于文档内容字数是否超过 max 阈值,来判断编辑器是否可以继续编辑 function maxSizePlugin(max) { return new Plugin({ props: { // 属性 editable 基于文档内容字数进行变动 editable(state) { return state.doc.content.size < max } } }) }DirectEditorProps
TypeScript interface
DirectEditorProps描述了编辑器视图的配置对象state属性:一个编辑器状态对象 state,用于设置该编辑器视图与哪个编辑器状态相关联/绑定plugins(可选)属性:一个数组(其元素是 plugin 插件对象),用于将一系列插件的 view 和 props 应用到视图对象上的注意
以上所设置的插件需要应用到编辑器视图上,所以这些插件(其配置对象)不能具有
state字段、filterTransaction字段或appendTransaction字段,否则会抛出错误具有上述字段的插件只能应用到编辑器状态上
dispatchTransaction属性:一个函数fn(tr: Transaction)用以设置当视图对象分发事务时,需要执行的一些额外处理
该属性值是一个函数fn(tr)其入参tr是一个事务对象。视图对象分发一个事务 dispatch transaction 时,在事务应用到编辑器的状态对象前,会先执行该回调函数。
该回调函数中this指向当前视图对象注意
请保证在该回调函数里最后执行了方法
this.updateState(state)以更新编辑器状态,该方法的入参state是(应用了视图所分发的事务tr后所生成的)新的编辑器状态ts// 应用了视图所分发的事务 `tr` 后生成新的编辑器状态对象 state const newState = view.state.apply(tr); // 也可以使用 applyTransaction 分发事务 `tr`,但需要解构来获取新的编辑器状态对象 // const {state, transactions} = view.state.applyTransaction(tr); // 更新编辑器状态 view.updateState(newState);
它其实继承自另一个 TypeScript interface
EditorProps,即除了上述两个属性,还可以用 interfaceEditorProps所定义的属性和方法来配置编辑器视图与
EditorProps(它也可用于配置 plugin 插件)相比,DirectEditorProps接口额外添加了的一些属性和方法,只能直接用于设置编辑器视图 view(而不能用于配置 plugin 插件)EditorProps
TypeScript interface
EditorProps<P=any>描述了编辑器视图或插件的配置对象提示
EditorProps<P=any>是一个泛型接口,可以设置类型变量P(默认值是any),它表示该接口所约束的配置对象的方法(例如以handler为前缀的一系列属性)里的this所指向的数据类型如果
EditorProps用于描述插件的配置对象,则该类型变量传入的是插件对象说明
其中以
handler为前缀的属性,是用于为相应事件设置处理函数(例如handleKeyDown用于响应按键事件),当事件分发时每次只能有一个处理函数在执行,会先执行在视图对象中设置的相应的事件处理函数,然后是在插件中设置的相应的事件处理函数(会根据插件注册的先后顺序,依次执行),直到其中一个事件处理函数返回true为止(表示该事件已响应/处理)而对于其他非函数型的属性,如果在多个插件中都进行了配置,会采用第一个设置值
以下属性和方法与事件监听处理相关
handleDOMEvents(可选)属性:它是一个对象,以键值对的方式为不同的 DOM 事件设置处理函数ts// 属性值是一个对象 // 其中属性名是一个表示 DOM 事件类型的字符串,值是响应函数 { [event in keyof DOMEventMap]: fn(view: EditorView, event: DOMEventMap[event]) → boolean | undefined }
该对象的属性名是 DOM 事件名称(具体而言是 interfaceDOMEventMap所列出的键名,而该接口实际上继承自 TypeScript 的内置 interfaceHTMLElementEventMap,所以可以查看这个列表来看看该对象的属性名有哪些备选项)
属性值是事件处理函数fn(view: EditorView, event: DOMEventMap[event])第一个参数view是编辑器视图对象;第二个参数event是当前分发的事件对象。最后应该返回一个布尔值,以表示该处理函数是否已经响应/处理完成了该事件注意
虽然后文列出的一系列
handle为前缀的属性也是用于设置事件处理函数,但是与该属性handleDOMEvents所设置的事件处理函数有所不同属性
handleDOMEvents灵活性更大,即可以为各种不同类型的事件设置处理函数,而后文列出的一系列handle为前缀的属性一般只能针对某一种类型的事件设置处理函数而在相应的事件分发时,通过以上属性
handleDOMEvents所设置的处理函数会先调用,(假如还通过后文列出的一系列handle为前缀的属性设置了事件处理函数,它们之后才会被调用)通过以上属性
handleDOMEvents所设置的事件处理函数,并没有针对事件的默认行为进行处理,如果需要阻止事件的默认行为,需要在事件处理函数中调用方法event.preventDefault();而通过后文列出的一系列handle为前缀的属性所设置的事件处理函数,被调用前视图对象会自动先调用event.preventDefault()以阻止事件触发浏览器的相关默认行为(即让编辑器在接收到用户的交互时,仅通过 Prosemirror 来处理)handleKeyDown(可选)属性:一个事件处理函数fn(view, event)当编辑器接收到keydown事件时会调用该处理函数,第一个参数view是编辑器视图对象,第二个参数event是 KeyboardEvent 键盘事件对象。最后返回一个布尔值或undefined,以表示该处理函数是否已经响应/处理完成了该事件handleKeyPress(可选)属性:一个事件处理函数fn(view, event)当编辑器接收到keypress事件时会调用该处理函数,第一个参数view是编辑器视图对象,第二个参数event是 KeyboardEvent 键盘事件对象。最后返回一个布尔值或undefined,以表示该处理函数是否已经响应/处理完成了该事件handleTextInput(可选)属性:一个事件处理函数fn(view, from, to, text)当编辑器接收到input事件时会调用该处理函数,第一个参数view是视图对象;第二、三个参数from和to是数值(符合 index schema 规则,表示文档的位置),分别表示在进行文本输入时编辑器选区的所覆盖的文档范围的 lower boundary 下界(在文档中较前的位置)和 upper boundary 上界(在文档中较后的位置);第四个参数text是字符串,表示用户输入的文本。注意
该函数会在输入内容应用/显示到页面之前先执行。
函数最后应该返回一个布尔值或
undefined,如果返回true则 Prosemirror 会阻止input事件的默认行为。💡 该方法一般用于拦截输入,以便根据用户原始输入的内容生成相应的内容,例如自动转换 markdown 语法,自动填充等。handleClickOn(可选)属性:一个事件处理函数fn(view, pos, node, nodePos, event, direct)当编辑器接收到click事件时,该事件从内往外冒泡时,其路径上所经过的节点(编辑器的节点)都调用一遍该处理函数,各参数的具体说明如下:- 第一个参数
view是编辑器视图对象 - 第二个参数
pos是一个数值,表示点击的位置(符合 index schema 规则,表示文档的位置) - 第三个参数
node是事件冒泡过程当前所到达的节点对象 - 第四个参数
nodePos是一个数值,表示当前节点在编辑器中的位置(符合 index schema 规则,表示文档的位置) - 第五个参数
event是 MouseEvent 事件对象 - 第六个参数
direct是一个布尔值,表示当前节点是否为正好被点击的(即最内层的)节点
最后返回一个布尔值或undefined,以表示该处理函数是否已经响应/处理完成了该事件- 第一个参数
handleClick(可选)属性:一个事件处理函数fn(view, pos, event)当编辑器接收到click事件时调用该处理函数,各参数的具体说明如下:- 第一个参数
view是编辑器视图对象 - 第二个参数
pos是一个数值,表示点击的位置(符合 index schema 规则,表示文档的位置) - 第三个参数
event是 MouseEvent 事件对象
最后返回一个布尔值或undefined,以表示该处理函数是否已经响应/处理完成了该事件区别
该属性所设置的处理函数也是在编辑器接收到
click事件后执行的,但与handleClickOn属性所设置的事件处理函数有点不同该属性的事件处理函数是在编辑器被点击后执行一次,且在
handleClickOn属性所设置的事件处理函数调用完之后才执行。- 第一个参数
handleDoubleClickOn(可选)属性:一个事件处理函数fn(view, pos, node, nodePos, event, direct),与属性handleClickOn类似,不过它是针对dblclick事件的。最后返回一个布尔值或undefined,以表示该处理函数是否已经响应/处理完成了该事件handleDoubleClick(可选)属性:一个事件处理函数fn(view, pos, event),与属性handlerClick类似,不过它是针对dblclick事件的。最后返回一个布尔值或undefined,以表示该处理函数是否已经响应/处理完成了该事件区别
该属性的事件处理函数是在编辑器被点击后执行一次,且在
handleDoubleClickOn属性所设置的事件处理函数调用完之后才执行。handleTripleClickOn(可选)属性:一个事件处理函数fn(view, pos, node, nodePos, event, direct),与属性handleClickOn类似,不过它是针对鼠标连续点击三次的场景(一般默认行为是选中所点击的整个段落)。最后返回一个布尔值或undefined,以表示该处理函数是否已经响应/处理完成了该事件提示
DOM 并没有提供与 triple click 相关的原生事件,根据 ProseMirror 的源码通过点击位置和间隔时间(判定为连续点击的最大间隔阈值是 500ms)来判定用户是否连续点击鼠标三次
另外根据 StackOverflow 的一个回答,其实可以基于鼠标事件对象的属性
event.detail来判断是点击的次数,它是一个数字,表示 current click count。但是可能由于不同浏览器对于连续点击的判断(最大间隔阈值)有所不同,ProseMirror 在 prosemirror-view 模块内部自己实现了判断单击、双击、三击的逻辑,以确保编辑器在不同浏览器上提供一样的交互体验
handleTripleClick(可选)属性:一个事件处理函数fn(view, pos, event),与属性handlerClick类似,不过它是针对鼠标连续点击三次的场景(一般默认行为是选中所点击的整个段落)。最后返回一个布尔值或undefined,以表示该处理函数是否已经响应/处理完成了该事件区别
该属性的事件处理函数是在编辑器被点击后执行一次,且在
handleTripleClickOn属性所设置的事件处理函数调用完之后才执行。handlePaste(可选)属性:一个事件处理函数fn(view, event, slice)当编辑器接收到paste事件时调用该处理函数,各参数的具体说明如下:- 第一个参数
view是编辑器视图对象 - 第二个参数
event是 ClipboardEvent 事件对象 - 第三参数
slice是一个 slice 对象(它是 ProseMirror 对粘贴内容解析后生成的)
提示
如果希望获取(剪切板)粘贴的原始内容,可以调用粘贴事件对象的方法
event.clipboardData.getData()来获取
最后返回一个布尔值或undefined,以表示该处理函数是否已经响应/处理完成了该事件- 第一个参数
handleDrop(可选)属性:一个事件处理函数fn(view, event, slice, moved)当编辑器接收到drop事件时调用该处理函数,各参数的具体说明如下:- 第一个参数
view是编辑器视图对象 - 第二个参数
event是 DragEvent 事件对象 - 第三参数
slice是一个 slice 对象(它是 ProseMirror 对粘贴内容解析后生成的) - 第四个参数
moved是一个布尔值,以表示拖放的内容是否源于编辑器内,如果值为true即表示拖拽移动的内容是来自编辑器当前选区,在移动后原来的内容默认应该被移除(以实现编辑器的内容从一个位置移动到另一个位置的效果)
最后返回一个布尔值或undefined,以表示该处理函数是否已经响应/处理完成了该事件- 第一个参数
handleScrollToSelection(可选)属性:一个事件处理函数fn(view)当视图对象尝试将编辑器的选区滚动到视窗中时调用该处理函数(一般会通过调用事务的方法tr.scrollIntoView()在编辑器状态更新完成后,将选区滚动到视图窗口中),参数view是编辑器的视图对象
函数最后应该返回一个布尔值,如果为true就表示阻止默认的滚动行为,具体如何响应就可以由该处理函数来决定;如果为false表示它不阻止默认的滚动(或让其他事件处理函数来进行处理)createSelectionBetween(可选)属性:一个函数fn(view, anchor, head)用于覆写/自定义编辑器(根据 DOM 选区)创建选区的行为。各参数的说明如下:- 第一个参数
view是视图对象 - 第二、三个参数
anchor和head都是一个 resolvedPos 对象,分别表示 DOM 选区的锚点和动点经过 ProseMirror 解析后的结果
最后返回一个 selection 选区对象(以在编辑器状态对象中表示/保留当前 DOM 选区)- 第一个参数
domParser(可选)属性:一个domParser对象,用于为编辑器设置自定义的 DOM 解析器(将 DOM 解析为编辑器可识别的格式)提示
当 editable DOM 改变时,编辑器就会使用该属性所设置的 DOM 解析器读取页面内容,将页面的 DOM 转换为编辑器可识别的格式,如 node 节点对象、slice 切片对象等
默认的 DOM 解释器是通过调用
DomParser类的静态方法DomParser.fromSchema(schema)(基于 schema 数据结构的约束对象)所生成的
以下属性和方法与剪切板相关
transformCopied(可选)属性:一个函数fn(slice, view)当往剪切板复制内容时,会先调用该函数。第一个参数slice是一个 slice 切片对象(表示需要复制进剪切板的内容),第二个参数view是编辑器视图对象
最后返回一个(经过转换处理)slice 切片对象
一个常见的应用场景是将 HTML 内容粘贴到编辑器(进行解析前)先进行「清洗」操作transformPastedHTML(可选)属性:一个函数fn(html, view)当往编辑器粘贴 HTML 格式的文本时,在 domParser 解析这些内容之前会先调用该函数。各参数的具体说明如下:- 第一个参数
html是一段 HTML 字符串 - 第二个参数
view是编辑器视图对象
最后返回一个(经过转换处理)表示 HTML 的字符串
一个常见的应用场景是将 HTML 内容粘贴到编辑器(进行解析前)先进行「清洗」操作- 第一个参数
transformPastedText(可选)属性:与前一个属性transformPastedHTML类似也是对粘贴的内容进行预处理,但它针对的(粘贴内容)是纯文本。一个函数fn(text, plain, view)当往编辑器粘贴纯文本时,在编辑器使用后述属性clipboardTextParser所设置的方法解析这些内容(成为一个 slice 切片对象)之前会先调用该函数。其中第二个参数plain是一个布尔值,表示是否将所粘贴的内容强制视为纯文本 ❓ 最后返回一个(经过转换处理的)表示纯文本的字符串transformPasted(可选)属性:一个函数fn(slice, view)当通过复制粘贴或拖放的方式将内容插入到编辑器之前会先调用该函数。
各参数的具体说明如下:- 第一个参数
slice是一个 slice 切片对象(表示需要插入的内容) - 第二个参数
view是编辑器视图对象
最后返回一个(经过转换处理)slice 切片对象提示
可以将其看作是一个通用型的粘贴内容转换器
而属性
transformPastedHTML或属性transformPastedText则分别只针对 HTML 或纯文本设置粘贴内容转换器- 第一个参数
clipboardParser(可选)属性:一个domParser对象,用于为剪切板设置自定义的 DOM 解析器
当编辑器需要从剪切板读取内容(且内容是 HTML 格式)时,例如执行粘贴操作,会使用该解析器(将 DOM 解析为编辑器可识别的格式)
如果不设置该属性,则默认采用前述属性domParser所设置的 DOM 解析器来作为剪切板的 DOM 解析器clipboardTextParser(可选)属性:一个函数fn(text, $context, plain, view)用于为剪切板设置自定义的纯文本解析器
当编辑器需要从剪切板读取内容(且内容是纯文本)时,例如执行粘贴操作,会使用该解释器,将纯文本解析为 slice 切片对象
各参数的具体说明如下:- 第一个参数
text是字符串,剪切板中需要解析的内容 - 第二个参数
$context是一个 resolvedPos 对象(表示光标的位置 ❓) - 第三个参数
plain是一个布尔值,如果要将所粘贴的内容强制视为纯文本 ❓ 则该值设置为true - 第四个参数
view是编辑器视图对象
最后返回一个 slice 对象提示
该解析器会在前述属性
transformPastedText所设置的转换器(对纯文本进行转换处理)执行完成后再运行默认的剪切板纯文本解析器的行为是将每个纯文本段落包裹到一个
<p>标签内,将它们转换为 HTML 格式(实际是返回一个 slice 对象)然后 ProseMirror 再调用前一个属性
clipboardParser所设置的 DOM 解析器来解析这些 HTML 文本- 第一个参数
clipboardSerializer(可选)属性:一个domSerializer对象,用于为剪切板设置自定义的 DOM 生成器/序列化器
当从编辑器复制内容到剪切板时,会调用该 DOM 序列化器对内容进行序列化(将编辑器数据,如 node 节点对象或 fragment 片段对象,序列化为 HTML),再添加到剪切板中
默认的 DOM 序列化器是通过调用DOMSerializer类的静态方法DOMSerializer.fromSchema(基于 schema 数据结构的约束对象)所生成的提示
由于序列化只会调用该 DOM 序列化器的一个方法
domSerializer.serializedFragment()即可完成,所以配置该属性时(除了提供一个 DOMSerializer 类实例)也可以提供一个只具有属性serializedFragment的对象,就可以实现类似 DOM 序列化器的功能clipboardTextSerializer(可选)属性:一个函数fn(slice, view)用于为剪切板设置自定义的纯文本序列化
当需要将编辑器选区的内容以纯文本的形式复制到剪切板时,会调用该纯文本序列化器,将选区内容转换为字符串
第一个参数slice是 slice 切片对象(表示选区内容),第二个参数view是编辑器视图对象提示
如果没有设置该属性,则默认采用选区所属的父节点 ❓ 的方法
parentNode.textBetween()获取选区范围的文本内容
以下属性和方法与自定义视图相关
nodeViews(可选)属性:它是一个对象{nodeName: NodeViewConstructor}以键值对的形式列出一系列不同类型的节点的视图构建函数(这些节点采用自定义的渲染展示方式和交互行为方式),其中键是节点的名称,值是对应的视图构建函数
视图构建函数NodeViewConstructor(node, view, getPos, decorations, innerDecorations)各参数的具体说明如下:- 第一个参数
node是节点对象 - 第二个参数
view是编辑器视图对象 - 第三个参数
getPos是一个方法,通过调用它可以获取节点在文档中的位置(返回一个数值,符合 index schema 规则;如果节点脱离了文档,则该方法返回undefined),如果需要分发 transaction 更新节点时知道它的位置很有用 - 第四个参数
decorations是一个数组,它的元素是 node decoration 节点装饰器对象或 inline decoration 内联装饰器对象,以表示应用到该节点的一系列装饰器提示
节点视图一般视为「隔离独立」在编辑器文档里,所以这些装饰器通常并不对其有影响,可以忽略该参数
但是有时候它们也可以看作是提供额外的上下文语境,例如在
plugin.props的decoration属性上,可以通过构造 decoration 的时候添加一些额外的信息,然后在这个参数中拿到这些信息 - 第五个参数
innerDecorations是一个对象(符合 TypeScript interfaceDecorationSource,规定了一些方法用于处理装饰器),表示节点内容中包含的装饰器提示
如果节点并没有内容或属性
contentDOM,则该参数可以忽略不考虑,因为编辑器会将装饰器自动绘制到节点视图中;如果节点具有内容或属性contentDOM,例如在节点视图中内嵌一个编辑器,则需要考虑如何处理这些装饰器
视图构建函数返回值是一个对象(符合 TypeScript interfaceNodeView)称为「节点视图」NodeView
节点对象一般使用(在 schema 数据约束对象中)相应节点类型 nodeType 的属性
toDOM)在页面上渲染出 DOM 元素,默认由 ProseMirror 来控制节点如何在页面显示和响应用户的操作但有时候场景需要对节点的视图有更高的控制自由度,例如为用户提供特殊的交互界面,可以使用该属性为特定类型的节点设置自定义的 UI 渲染形式和交互方式(而不采用 schema 中所定义的方法来构建节点视图)
TypeScript interface
NodeView描述了一个节点的视图对象dom属性:一个 DOMNode,用于设置该节点渲染到页面的形式(相当于节点配置对象 nodeSpec 的属性toDOM的作用,如果节点设置了 nodeView 就会使用该属性进行覆盖)contentDOM(可选)属性:一个 HTMLElement,表示节点内容渲染到页面的哪个 DOM 内注意
该属性需要在设置了上一个属性
dom,且该节点视图所对应的节点不是叶子节点才生效
当设置了该属性,Prosemirror 会自动将节点内容(即它的子节点)渲染到该指定的 DOM 内;如果没有设置该属性,则需要开发者(构建节点视图时)手动设置如何渲染该节点的内容
提示
为了兼容旧版本,也支持在该属性中为不同类型的样式标记设置视图构建函数,但是更推荐通过下文所介绍的属性
markViews来设置但是对于样式标记视图的构建函数,它的返回的对象只能包含两个属性
{dom: DOMNode, contentDOM?: HTMLElement},以下列出的其他属性/方法都不支持配置update(可选)属性:一个函数fn(node, decorations, innerDecorations)以更精细地控制 nodeView 如何更新(对应的 DOM 元素),该函数最后返回一个布尔值以表示是否更新完成
该函数的各参数的具体说明如下:- 第一个参数
node是该节点视图所对应的 node 节点对象(具有最新状态的,甚至可能和更新前的节点类型不一样) - 第二参数
decorations是一个数组,它的元素是 decoration 装饰器对象,以表示应用到该节点的一系列装饰器(它们通常并不影响节点视图的更新,一般可以忽略它们) - 第三个参数
innerDecorations是一个对象(符合 TypeScript interfaceDecorationSource),表示应用于节点内容(子节点)上的装饰器(通常不需要考虑它们对节点视图更新的影响)
该函数最后返回值一个布尔值,以表示节点视图所对应的节点是否可以进行更新提示
如果该节点视图设置了属性
contentDOM(或没有设置属性dom),则该节点视图所对应的节点的子节点的更新由 Prosemirror 自动处理
通过该方法可以按需复用当前的 nodeView 实例,以提高编辑器的性能,该函数最后返回一个布尔值以表示是否更新完成- 如果该方法返回
true就指当前的 nodeView 可以通过更新,来表示它所对应的节点的新状态,即该 nodeView 实例可以复用(一般该 nodeView 在页面所对应的 DOM 元素也得以复用) - 如果返回
false表示无法通过更新当前的 nodeView 来表示它所对应的节点的新状态(则需要重新调用构造函数创建一个新的 nodeView 实例来表示它所对应的节点的新状态,一般该 nodeView 在页面所对应的 DOM 元素也需要重绘)
注意
应该避免在
update函数里dispatch事务,这可能触发 infinite loop- 第一个参数
selectNode(可选)属性:一个函数fn(),用于自定义该节点选中时的状态(即节点选区的展示方式)deselectNode(可选)属性:一个函数fn(),用于自定义该节点取消选中时的状态。一般与上一个属性selectNode配合使用(以移除所设置的渲染效果)setSelection(可选)属性:一个函数fn(anchor, head, root),用于自定义在节点里进行框选(创建选区)的交互行为提示
当用户框选节点内容时,默认情况下会根据参数
anchor和head所表示的位置创建一个 DOM 选区,但可以通过该属性覆盖这个默认行为
该函数的各参数的具体说明如下:- 第一、二个参数
anchor和head是数值,分别表示选区相对于该节点开头位置的锚点和动点位置(符合 index schema 规则,表示文档的位置) - 第三个参数
root是该选区所在的 DOM 节点
- 第一、二个参数
stopEvent(可选)属性:一个函数fn(event)(参数event是在该节点视图上所触发的的事件对象),用于阻止某些/全部来自该节点视图的事件,让它们不被编辑器所处理。最后返回值是一个布尔值,如果为true则阻止事件冒泡ignoreMutation(可选)属性:一个函数fn(dom.MutationRecord),用于设置编辑器是否忽略该节点视图所对应的 DOM 的变化(包括选区的变化)
函数fn(dom.MutationRecord)的参数是一个 DOM MutationObserver 变化记录。当 DOM 变化时,或节点视图层内的选区变化时(此时 DOM 的变化记录对象的属性type其值为selection),会调用该函数
该函数最后返回一个布尔值,如果为false则编辑器会响应 DOM 变化,重新读取选区,或重新解析发生变化的 DOM 节点;如果为true则编辑器会忽略 DOM 变化destroy(可选)属性:一个函数fn(),当节点视图(或整个编辑器)销毁时会执行该函数
- 第一个参数
markViews(可选)属性:它是一个对象{markName: MarkViewConstructor}以键值对的形式列出一系列不同类型的样式标记的视图构建函数(和前一个属性nodeViews类似,为这些样式标记提供自定义的渲染展示方式,但是不能定制交互行为),其中键是样式标记的名称,值是对应的视图构建函数
视图构建函数MarkViewConstructor(mark, view, inline)各参数的具体说明如下:- 第一个参数
mark是样式标记对象 - 第二个参数
view是编辑器视图对象 - 第三个参数
inline是一个布尔值,表示该样式标记的内容是否为 inline 内联类型
视图构建函数返回值是一个对象,只能包含两个属性{dom: HTMLElement, contentDOM?: HTMLElement}(具体介绍可以查看 interfaceNodeView的相应属性dom和属性contentDOM,在样式标记视图中它们的含义是一致的)- 第一个参数
decorations(可选)属性:一个函数fn(state)(参数state是编辑器的状态对象),返回一个对象(符合 TypeScript interfaceDecorationSource),为编辑器视图设置所需应用的装饰器的合集editable(可选)属性:一个函数fn(state)(参数state是编辑器的状态对象),返回一个布尔值,用于设置编辑器的视图是否可以操作,即是否接受用户的交互修改编辑器的内容,如果为false则表示编辑器的内容不可以通过与视图交互直接修改attributes(可选)属性:有多种形式的属性值,用于设置编辑器所对应的 editable DOM 元素的属性 attributes
该属性值可以是一个对象,各属性名为 DOM 的 attribute 名称,属性值为 attribute 的值;也可以是一个一个函数fn(state)(参数state是编辑器的状态对象),返回一个对象,也是以键值对的形式为 DOM 元素设置属性 attributes注意
页面的 editable DOM 元素默认会具有
ProseMirror类名,并且当上一个属性editable为true时会为 editable DOM 元素添加contentEditableattribute通过该属性所设置的 class 类名将以追加的方式添加到 DOM 元素上,对于其他的 attribute,则会采用第一个设定的值(例如不同插件设置了同一个 attribute,则优先注册的插件所设定的值会被采用)
scrollThreshold(可选)属性:设置触发滚动的阈值(以像素为单位,默认值为0),当光标(文本选区的一种状态)与 viewport 边缘相距小于该阈值时,会触发编辑器页面滚动以确保光标可见
该属性值可以是一个数值,设置光标与视窗底部相距的阈值;或一个对象{top, right, bottom, left}设置光标与视窗的四周相距的阈值。说明
该阈值的默认值是
0,所以在使用键盘的上下键在编辑器中导航时,或编辑时按下enter键换行时,当光标已经抵达视窗的底部,编辑器页面就会滚动,确保光标始终在视窗内scrollMargin(可选)属性:设置当编辑器页面滚动以让光标置于视窗内,究竟滚动到何种程度(以像素为单位,默认值为5)才停止滚动
该属性值可以是一个数值,设置光标与视窗底部的距离;也可以是一个对象{top, right, bottom, left}分别设置光标与视窗四周的距离
editorView 编辑器视图对象包含一些属性和方法:
state属性:编辑器状态对象,表示当前编辑器视图与哪个编辑器状态相绑定dom属性:一个 HTMLElement,它是一个 editable DOM 元素, ⚠️ 它在页面显示出编辑器文档,一般不应该直接操作该 DOM 及其内容editable属性:一个布尔值,表示编辑器目前是否可编辑dragging属性:一个对象{slice: Slice, move: boolean}或为null,表示拖拽相关信息
在视图中进行拖拽交互时,该属性值是一个对象{slice: Slice, move: boolean}其中属性slice是一个 slice 切片对象,它包含拖拽的内容;另一个属性是move是一个布尔值,表示拖拽的方式(如果为true则以 moved 的方式将原有的内容移动到其他位置,即拖拽结束时原来位置的内容会删除掉;如果为false则以 copied 的方式将内容移动到其他位置,例如从外部拖拽导入内容)
当视图没有进行拖拽交互时,该属性是nullcomposing属性:一个布尔值,以表示compositionEvent事件是否在激活(该事件表示用户正在间接输入文本,即在键盘的输入和最终输出到页面的值并不一致,例如使用 CJK 中日韩输入法时)props属性:一个对象(符合 TypeScript interfaceDirectEditorProps),表示编辑器视图的配置对象update(props)方法:用于更新整个编辑器视图的配置对象。其中入参prop是新的配置对象(符合 TypeScript interfaceDirectEditorProps),该操作会立即触发(编辑器视图在页面所对应)DOM 进行更新setProps(props)方法:用于更新部分配置参数。其中参数props是一个对象(含有 TypeScript interfaceDirectEditorProps所描述的部分属性)提示
该方法可用于更新配置对象的部分属性值,对于其他属性则使用原值
该方法相当于前一个方法
view.update(Object.assign({}, view.props, props))someProp(propName, f?)方法:遍历编辑器视图配置对象中的某个属性propName的值视图配置对象具有多个值
可以在使用方法
new EditorView(place, props)初始化编辑器时设置配置对象。此外由于 Prosemirror 支持插件系统,而且可以通过插件对编辑器视图进行配置,即可以在多处进行编辑器视图配置,所以配置对象的某个属性可能「具有多个值」(虽然最终采取优先级最高的值)在遍历编辑器视图配置对象的某个属性的值时,该值可能是从视图的配置对象中获取的,也可能是从插件的配置参数中获取的(而且插件可能有两个来源,可以是应用到 state 编辑器状态对象上的,也可以是应用到 view 编辑器视图对象上的)
通过各种方法设置属性值的优先级:
- 在初始化编辑器视图时
new EditorView(place, props)配置对象props中的属性值优先级最高 - 应用到编辑器视图上的插件所设置的属性值优先级次之(如果有多个插件配置同一个属性,则根据插件的注册顺序来判断优先级)
- 应用到编辑器状态上的插件所设置的属性值优先级最低
该方法的各参数的具体说明如下:- 第一个参数
propName是需要遍历值的属性 - 第二个(可选)参数
f(value)是一个函数。如果当前所遍历的属性值不是undefined则调用该函数,其入参value就是当前所遍历到的属性值。如果该函数最后返回的是一个 truthy 的值,则遍历结束并返回该值;如果函数返回的是一个 falsely 的值,则继续遍历下一个属性值。如果没有设置该函数,则直接返回第一个属性值(优先级最高)
- 在初始化编辑器视图时
updateState(state)方法:用于更新前文所述的属性state(而并不触及其他属性),入参state是一个 state 编辑器对象hasFocus()方法:一个布尔值,查看编辑器的视图是否获得焦点focus()方法:调用该方法让编辑器的视图获得焦点root属性:编辑器所在页面的 document 对象,或一个 shadow DOM(如果编辑器是内嵌在页面中)updateRoot()方法:当将一个已存在的编辑器视图挂载/应用到页面的另一个 DOM 元素(或 shadow DOM)上时,调用该方法以更新前一个属性rootposAtCoords(coords)方法:根据给定的 viewport 视窗位置coords(它是一个对象{left: number, top: number}表示视窗中的一个位置,单位是像素),获取编辑器文档中与之距离最近的位置
该方法的返回值有多种类型,与给定的 viewport 位置相关:- 如果给定的 viewport 位置不在编辑器内部,则返回
null - 如果给定的 viewport 位置在编辑器内部,则返回一个对象
{pos: number, inside: number}以描述文档中的位置,其中属性pos是一个数值(符合 index schema 规则)表示文档的位置;如果该位置在一个节点里,那么属性inside(一个数值)表示距离该节点开头的偏移量(所以它是0或正数,符合 index schema 规则 ❓ ),如果该位置是在文档顶级节点上(而不在任何子节点内部),则属性inside为-1
提示
该方法一般用在事件处理函数中,可以通过事件对象
event的属性clientX和clientY得出事件发生在 viewport 的哪个位置,然后获取编辑器文档中与之距离最近的位置- 如果给定的 viewport 位置不在编辑器内部,则返回
coordsAtPos(pos, side?)方法:根据给定的文档位置pos,获取该位置在 viewport 所对应的矩形框(包含坐标信息)
第一个参数pos是一个数值(符合 index schema 规则)表示文档的位置;当该位置正好处于前后元素不连续的(例如在该位置使用了 widget decoration ❓),则可以通过第二个(可选)参数side(一个数值,默认值为1)设置应该获取哪一侧的坐标信息,当该参数值小于0时则采用靠前的一侧;否则采用靠近的一侧
该方法最后返回一个对象{left: number, right: number, top: number, bottom: number}表示一个 viewport 的矩形框,其中属性left和right总是相等的,因为该矩形框描述的是类似光标的位置nodeDOM(pos)方法:根据给定的文档位置pos,获取所对应的 DOM 元素
参数pos是一个数值(符合 index schema 规则,表示文档的位置)
该方法的返回值有两种类型,与给定的文档位置相关:- 如果给定的文档位置正好是在一个节点前,则返回该位置后面的节点在页面所对应的 DOMNode
- 如果给定的文档位置在节点里(而不是正好在节点前面),或在一个 opaque node view 不透明的节点视图里,则返回
nullopaque node view
opaque node view 不透明的节点这里的「不透明」是指该节点视图不包含属性
contentDOM,则 ProseMirror 无法控制其内容的渲染,所以该节点视图对于 ProseMirror 而言是「不透明」的
注意
虽然通过该方法可以获取 DOM 元素,但不应该直接更改 DOM 元素(包括其内容、属性、样式等可以触发 DOM 更新的变动),因为修改可能随着节点(更新所触发的)重绘而覆盖掉
可以使用该方法读取一些关于该位置所对应的 DOM 元素的信息,例如通过
dom.getBoundingClientRect获取 DOM 元素在页面的大小,和前述的方法coordsAtPos(pos)类似domAtPos(pos, side?)方法:根据给定的文档位置pos,获取相应的 DOM 元素
第一个参数pos是一个数值(符合 index schema 规则,表示文档的位置);第二个(可选)参数side是一个数值(默认值为0),表示偏向于获取哪一侧的 DOM 元素,如果该参数小于0则偏向于获取前侧的 DOM 元素(左侧),如果该参数大于0则偏向于获取后侧的 DOM 元素(右侧),如果该参数为0则获取距离文档位置最近的 DOM 元素
该方法最后返回值是一个对象{node: DOMNode, offset: number}属性node是所获取到的 DOM 元素,另一个属性offset是一个数值,表示该 DOM 元素(在网页的结构树中,相对于其父元素)的索引值注意
虽然通过该方法可以获取 DOM 元素,但不应该直接更改 DOM 元素(包括其内容、属性、样式等可以触发 DOM 更新的变动),只应该使用该方法读取一些关于该位置所对应的 DOM 元素的信息
posAtDOM(node, offset, bias?)方法:根据给定的(编辑器在页面上的)nodeDOM 元素,获取相应的文档位置,返回一个数值符合 index schema 规则,表示文档的位置)
该方法的各参数的具体说明如下:- 第一个参数
node是 DOM 元素 - 第二个参数
offset是数值,表示在 DOM 元素里的偏移量(如果该 DOM 元素的内容是文本,则表示字符偏移量;如果是包含一系列的子元素,则表示相对于其父节元素的索引值),用于设置具体对 DOM 元素里的哪一个位置进行查询/转换 - 第三个(可选)参数
bias是一个数值(默认值为-1),用于指定查询/获取的位置偏向 DOM 的哪一侧。主要针对叶子元素(即不包含内容的 DOM 元素,例如<img>图像元素),如果参数值大于0则获取/查询叶子元素的的右侧在文档中的相应位置;否则获取/查询叶子元素的左侧
提示
更推荐基于编辑器文档结构(树形结构或扁平结构)来查询/获取位置,更准确且性能会更好
但是在一些特殊的场景,例如在与编辑器交互时只知道事件对象
event,则可以通过属性event.target获取到页面上的 DOM 元素,再通过该方法「逆向」查询/获取到编辑器文档的相应位置信息,但是该方式需要 ProseMirror 对编辑器在页面上的所有 DOM 依次进行检索,比较消耗性能- 第一个参数
endOfTextblock(dir, state?)方法:用于判断光标(选区)是否在文本块 textblock 的边界处提示
文本块 textblock 一般是指以文本作为内容的节点,例如
paragraph段落节点
该方法的各参数的具体说明如下:- 第一个参数
dir可以是up、down、left、right、forward、backward这六个值之一,表示应该把光标往哪个方向上移动一个单位进行测试 - 第二个(可选)参数
state是一个编辑器状态对象(默认传入的是当前编辑器的状态对象,但也可以传入其他的编辑器状态对象,以判断该状态下的选区情况)
该方法最后返回一个布尔值,以表示如果光标移动一个单位后,是否会移出 textblock 所在的父节点,如果为true则表示光标在该方向上处于 textblock 的边界;否则返回false- 第一个参数
parseHTML(html, event?)方法:对给定的html(一个字符串,表示 HTML 格式的文本)执行编辑器与粘贴相关的逻辑(默认的粘贴流程:从剪切板读取内容,解析为 slice 切片对象,替换当前编辑器选区的内容)。如果设置了第二个(可选)参数event(一个 ClipboardEvent 剪切板事件对象),则会将它传递给handlePaste处理函数(该函数用于覆盖前述的默认粘贴流程,以自定义粘贴行为)。最后返回一个布尔值表示是否粘贴成功pasteText(text, event?)方法:与前一个方法parseHTML类似,但是该方法是针对纯文本的,对给定的text(一个字符串,表示纯文本)执行编辑器与粘贴相关的逻辑。最后返回一个布尔值表示是否粘贴成功destroy()方法:将编辑器从页面移除,并销毁/清除编辑器中所有的 node views 节点视图对象isDestroyed属性:一个布尔值,表示编辑器是否被销毁dispatchEvent(event)方法:手动分发事件,用于调试dispatch(tr)方法:分发一个事务(参数tr是需要分发的 transaction 事务对象)提示
如果在编辑器视图的配置对象中设置了属性
dispatchTransaction,则会执行该属性所设置的函数fn(tr)(可以手动地、更精细地控制事务如何应用到编辑器的状态对象上,⚠️ 请保证该函数最后执行了this.updateState(state)方法来更新编辑器的视图);如果视图对象没有配置属性dispatchTransaction,则 ProseMirror 会自动将事务应用到编辑器的状态对象上,并自动调用updateState()方法来更新编辑器的状态
Decoration 类
Decoration 类可以影响节点在页面的渲染结果,但是不会修改节点在编辑器文档中的数据表示形式
装饰器用在哪里
使用方法 new EditorView(place, props) 创建编辑器视图时,在配置对象 props(符合的 TypeScript interface DirectEditorProps,继承自 interface EditorProps)的属性 decorations 中使用到装饰器(在属性 nodeView 中也有涉及)
Decoration 与 NodeView 区别
NodeView 节点视图和 Decoration 装饰器都可以对节点在页面的渲染形式进行自定义
NodeView 对于视图层的配置灵活度更高,还可以对视图层的交互方式进行自定义;而 Decoration 则适用于进行轻量级的 UI 定制,所以它被称作装饰器,它只是为页面添加一些「点缀物」,例如语法高亮,它只影响渲染输出效果而不会修改编辑器的文档内容/数据
该类提供了三种静态方法进行实例化,分别创建三种装饰器以适用于不同的场景:
Decoration.widget(pos, toDOM, spec?)静态方法:在给定位置pos创建一个挂件装饰器,它在页面渲染为一个 的 DOM 元素实例化的参数说明
- 第一个参数
pos是一个数值(符合 index schema 规则,表示文档的位置),用以设置该挂件装饰器应该放置在文档哪个位置 - 第二个参数可以是
toDom可以是一个函数fn(view, getPos)(它返回一个 DOMNode)或直接传递一个 DOM 元素,用于设置该挂件装饰器在页面如何渲染。
如果设置为函数fn(view, getPos)则它接受两个参数,第一个参数view是编辑器的视图对象,第二个参数getPos是一个方法,通过调用它可以获取该挂件装饰器在文档中的位置(返回一个数值,符合 index schema 规则)延迟渲染
更推荐使用函数的形式来指定挂件装饰器所对应的 DOM 元素,因为它会等待该 decoration 真正绘制到视图层时才被调用,以实现按需渲染
- 第三个(可选)参数
spec是一个对象,包括挂件装饰器的其他配置项side(可选)属性:一个数值,用于设置挂件装饰器与给定位置的哪一侧相关,以便处理涉及光标相关的操作
如果是负值则将挂件装饰器绘制在指定位置(前文所述的参数pos)光标的前侧,如果用户继续在该位置输入内容,会置于该装饰器的后面;如果是0(默认值)或正值则将挂件装饰器绘制在指定位置光标的后侧,如果用户继续在该位置输入内容,则会置于装饰器的前面
如果后一个属性marks设置为null时,则该属性也决定挂件装饰器会继承哪一侧节点所具有的 mark 样式标记。如果该属性值是负值,则会继承/应用位于挂件前面的节点所具有的样式标记;如果该属性值是正数,则会继承/应用位于挂件后面的节点所具有的样式标记多个挂件装饰器插入同一个位置
如果在给定位置插入多个挂件装饰器,则根据该属性
side的数值的大小来排序依次插入页面,即数值较小的挂件装饰器先插入页面。而对于该属性值相同的两个挂件装饰器,其插入页面的先后顺序不确定marks(可选)属性:一个数组,其元素是 mark 样式标记对象,用于设置应用在挂件装饰器周围的样式标记,即在挂件装饰器所在位置继续输入内容时,需要应用什么样式标记stopEvent(可选)属性:一个函数fn(event)(参数event是该挂件装饰器接收到的事件对象),用于阻止某些/全部来自该挂件装饰器的事件,让它们不被编辑器所处理。最后返回值是一个布尔值,如果为 true 则阻止事件冒泡ignoreSelection(可选)属性:一个布尔值(默认为false),用于设置是否忽略挂件装饰器里的选区变化。如果为true则 Prosemirror 会忽略挂件装饰器里的选区变化;否则 ProseMirror 会读取挂件装饰器的 DOM 选区,并更新编辑器状态的选区以实现同步key(可选)属性:一个字符串,为挂件装饰器设置唯一标识符提示
在判断装饰器是否需要重新渲染时,ProseMirror 默认以装饰器所对应的 DOM 元素作为标识符,将同一种类型的装饰器进行区分比较,以决定需要对哪一个进行重绘
也可以通过属性
key为装饰器设置一个字符串作为标识符,ProseMirror 就会以这个key值区分不同的装饰器(而不是它们的 DOM 元素),这在需要动态生成装饰器(且不想重用旧的 DOM 节点 ❓ )的场景中非常有用但请确保具有相同
key的装饰器是可以互换的,例如不同装饰器对事件处理行为有所不同,那它们就应该使用不同的key值destroy(可选)属性:一个函数fn(node)(参数node是 DOMNode,挂件装饰器所对应的 DOM 节点),当挂件装饰器(或整个编辑器)销毁时会执行该函数[string]自定义属性:还可以为挂件装饰器设置其他属性(属性名是字符串,属性值可以是任何数据类型),以添加额外的信息
- 第一个参数
Decoration.inline(from, to, attrs, spec?)静态方法:在给定范围创建一个行内装饰器,其作用是为给定范围中的行内节点添加特定的属性实例化的参数说明
- 第一、二个参数
from和to是一个数值(符合 index schema 规则,表示文档的位置),分别设置行内装饰器的起点和终点 - 第三个参数
attrs是一个对象(符合 TypeScript typeDecorationAttrs),用于为给定范围内的每个 inline 行内类型的节点(所对应的 DOM 元素)添加属性DecorationAttrs
TypeScript type
DecorationAttrs以键值对的方式描述要添加到装饰器(所对应的 DOM 元素)的 attributes 合集如果键名与某个 DOM attributes 一样,则会将它设置为 DOMNode 相应的 property 值,但有一些特例它们的处理方式有所不同:
nodeName(可选)属性:一个字符串(非null值),以指定 DOM 节点的类型,创建一个容器,用以包裹装饰器所指定范围内的节点(一般是针对节点装饰器而言 ❓ ),然后其他属性 attrs 的设置会应用于该容器上class(可选)属性:一个字符串,用以设置类名(可以设置多个 class 名,并用空格进行分隔),会以追加的方式添加到 DOM 元素上style(可选)属性:一个字符串,用以设置行内样式,会以追加的方式添加到 DOM 元素上
- 第四个(可选)参数
spec是一个对象,包括行内装饰器的其他配置项
- 第一、二个参数
Decoration.node(from, to, attrs, spec?)静态方法:在给定范围创建一个节点装饰器,其作用是为给定范围内的一个节点(即该范围与该目标节点在文档中的范围一致)添加特定的属性实例化的参数说明
- 第一、二个参数
from和to是一个数值(符合 index schema 规则,表示文档的位置),分别设置节点装饰器的起点和终点(它们需要正好精准地定位到一个目标节点的开始和结束位置) - 第三个参数
attrs是一个对象(符合 TypeScript typeDecorationAttrs,具体介绍可查看前文 inline decoration 行内装饰器相关部分),为目标节点(所对应的 DOM 元素)添加的属性 - 第四个(可选)参数
spec是一个对象,用于为节点装饰器添加一些附加的信息,该属性也用于节点装饰器之间的比较,以判断它们是否相同
- 第一、二个参数
decoration 装饰器对象包含一些属性和方法:
from属性:一个数值,表示该装饰器在文档中的开始位置(符合 index schema 规则,表示文档的位置)to属性:一个数值,表示该装饰器在文档中的结束位置(符合 index schema 规则,表示文档的位置)spec属性:一个对象,它表示在创建装饰器时所传递的配置参数spec。因此可以在创建装饰器时,设置一些额外的信息,然后在使用该装饰器时,可以通过该属性再获取这些附加的信息
DecorationSet 类
该模块还提供一个 DecorationSet 类,表示一个装饰器的合集,使用这种数据结构来组织装饰器,让 ProseMirror 的绘制算法可以更高效地对比和渲染它们(它实现了 TypeScript interface DecorationSource,即具有该类型约束所指定的属性和方法,还添加了额外的一些属性和方法)
注意
它是一个 persistent data structure 不可变的数据结构,即它不能直接改变,只能通过生成一个新的值来进行更新
DecorationSource
TypeScript interface DecorationSource 描述了两个方法:
map(mapping, node)方法:它根据文档发生的变更,对装饰器进行重新映射(更新它们的位置)。第一个参数mapping是一个 Mapping 对象;第二个参数node是一个 node 节点对象(根节点 ❓ 表示编辑器的文档)。最后返回一个经过映射更新的对象(它也符合 TypeScript interfaceDecorationSource)forChild(offset, child)方法:获取应用于给定节点child(内容里)的装饰器,第一个参数offset是开始检索的位置(符合 index schema 规则,表示文档的位置)
该接口由 class DecorationSet 实现,并应用在 nodeView 中
通过该类所提供的一些静态方法或属性进行实例化,得到一个 decorationSet 对象,以下称作「装饰器合集对象」
DecorationSet.create(doc, decorations)静态方法:通过给定的doc一个 node 节点(根节点,表示编辑器文档)和一系列的装饰器decorations(一个数组,元素是 decoration 装饰器对象)来创建一个装饰器合集DecorationSet.empty属性:一个空的装饰器合集。
decorationSet 装饰器合集对象包含一些属性和方法:
find(start, end, predicate)方法:从装饰器合集中,筛选出在给定范围内,且满足特定条件的装饰器。
各参数的具体说明如下:- 第一、二个(可选)参数
start和end都是一个数值(符合 index schema 规则,表示文档的位置),用以设置查找的范围(在该范围边界上的装饰器也会纳入考虑)。如果没有设置这两个参数,则默认查找整个文档里的装饰器 - 第三个(可选)参数
predicate是一个函数fn(spec)用以设置过滤条件。装饰器都会分别调用该函数,并将它的配置对象spec作为入参,这样就可以根据装饰器的信息对它们进行过滤筛选。该函数最后返回一个布尔值,表示该装饰器是否符合条件。如果没有设置该参数,则默认所有指定范围内的装饰器都符合条件。
该方法最后返回一个数组,其元素是符合条件的装饰器对象- 第一、二个(可选)参数
map(mapping, doc, options)方法:根据文档发生的变更,对装饰器进行重新映射(更新它们的位置)
各参数的具体说明如下:- 第一个参数
mapping是一个 Mapping 对象 - 第二个参数
doc是一个 node 节点对象(根节点 ❓ 表示编辑器的文档) - 第三个(可选)参数
options是一个对象,它包含一个(可选)属性onRemove用以设置当合集中任意一个装饰器被移除时,执行一些额外的操作,属性值是一个函数fn(decorationSpec)在映射过程中被移除的装饰器会分别调用该函数,入参是该装饰器的配置对象spec
该方法返回映射后的装饰器合集- 第一个参数
add(doc, decorations)方法:往合集中添加其他给定的一系列装饰器decorations(一个数值,元素是装饰器对象),构成一个新的合集。第一个参数doc传入的是一个 node 节点对象(根节点 ❓ 表示编辑器的文档),由于需要获取访问当前文档以正确地创建树形结构remove(decorations)方法:从合集中移除指定的一系列装饰器decorations(一个数值,元素是装饰器对象),构成一个新的合集。