path的概念
在 @ckeditor/ckeditor5-engine 中,path 是一个核心概念,用于表示在文档模型或视图树中某个节点的位置。path 的主要作用是帮助引擎精确地定位文档结构中的特定节点、元素或文本片段。
path 的定义
path 是一个数组,每个数组元素代表从树的根节点到目标节点的逐层索引。例如,在模型或视图树中,从根节点到某个子节点的路径可能是 [2, 3, 1],表示从根节点的第 2 个子节点开始,然后再从该子节点的第 3 个子节点进入,最后从这里进入第 1 个子节点。
path 的工作机制
-
层级结构的索引:
- 文档的模型或视图树是一个嵌套的层级结构,类似于 DOM 树。每个节点可以包含多个子节点,而
path就是通过逐层索引来找到目标节点的路径。 -
path数组中的每个元素代表在当前层级中目标节点的索引。例如,[2, 3, 1]表示从根节点的第 2 个子节点(索引为2的子节点)出发,进入该子节点的第 3 个子节点,再进入该节点的第 1 个子节点。
- 文档的模型或视图树是一个嵌套的层级结构,类似于 DOM 树。每个节点可以包含多个子节点,而
-
模型中的
path:- 在模型层中,
path用于标识文档结构中的位置。CKEditor 5 的文档模型是一棵树,path是描述树中节点相对根节点位置的唯一方法。 - 例如,假设模型文档结构如下:
root ├── paragraph (0) │ ├── text "Hello" (0) └── paragraph (1) └── text "World" (0)- 在这个模型中,
path为[0, 0]表示的是第一段落中的 "Hello" 文本节点,而[1, 0]表示的是第二段落中的 "World" 文本节点。
- 在这个模型中,
- 在模型层中,
-
视图中的
path:- 在视图层中,
path同样用于描述节点的层级关系。视图层的结构与模型类似,因此path在视图层的作用也类似,即用于定位 DOM 树中的具体节点或元素。
- 在视图层中,
使用场景
定位节点:
path用于标识和定位特定的节点,尤其是在需要修改文档内容或处理用户交互时。引擎会通过path来找到需要操作的模型或视图节点。节点操作:操作(
Operation)机制依赖path来描述文档的变更位置。例如,插入、删除、移动元素等操作都需要明确指出变更发生的位置,而path就是用于指定这些位置。选区管理:
path在选区管理中也至关重要。编辑器中的选区可以通过起始path和结束path来定义。通过记录选区的path,编辑器能够在模型和视图之间保持精确的同步。变更监听:在监听文档的变更时,
path用来标识发生变更的位置,开发者可以通过path来判断某个节点的具体位置,并根据需要进行操作。
例子:如何解析 path
假设你有以下模型结构:
root
├── heading (0)
│ ├── text "Title" (0)
├── paragraph (1)
│ ├── text "This is a test." (0)
└── paragraph (2)
├── text "Another paragraph." (0)
-
path = [0, 0]表示heading下的 "Title" 文本节点。 -
path = [1, 0]表示paragraph下的 "This is a test." 文本节点。 -
path = [2, 0]表示第二个段落中的 "Another paragraph." 文本节点。
总结path的作用
在 @ckeditor/ckeditor5-engine 中,path 是用于表示文档树中节点位置的关键工具。它通过层级索引的方式提供了一种准确、灵活的定位方法,能够有效地处理复杂文档结构中的节点操作、选区管理和变更监听。
before和after
在 CKEditor 5 的 @ckeditor/ckeditor5-engine 中,path 不仅用于表示特定节点的位置,还提供了一些相关的辅助工具和概念,如 before 和 after,用于描述相对于当前节点位置的特定插入点。这两个概念通常用于操作模型(Model)或视图(View)的节点,例如插入、删除、移动操作时,确保准确定位。
before 和 after 的含义
before:指当前节点之前的位置,即在树结构中该节点的左侧。它描述的是“在该节点的前一个位置”进行某种操作。after:指当前节点之后的位置,即在树结构中该节点的右侧。它表示“在该节点的后一个位置”进行某种操作。
这两个概念通常结合 path 来定义插入点或操作位置,用于在文档结构中进行各种修改操作,比如插入新内容或移动现有内容。
示例解析
假设有以下文档模型结构:
root
├── paragraph (0)
│ ├── text "Hello" (0)
├── paragraph (1)
│ ├── text "World" (0)
└── paragraph (2)
├── text "!" (0)
before
假设我们要在第一个段落之前插入一个新的段落:
- 段落 "Hello" 的
path是[0],即它在根节点的第 0 个位置。 -
before就是指在这个path之前的插入点。 - 如果我们要在段落 "Hello" 之前插入一个新的段落,我们可以选择
[0]作为插入点。这意味着新段落将插入到现有段落 "Hello" 的path = [0]之前,即[0]的before位置。
after
假设我们要在段落 "World" 之后插入一个新的段落:
- 段落 "World" 的
path是[1]。 -
after指的是在这个段落之后的位置,即段落path = [1]之后。 - 如果我们想在段落 "World" 之后插入一个新的段落,插入点将是
[2],因为这是path = [1]的after位置,即在它的后面进行插入。
使用 before 和 after 的实际场景
-
插入操作:
- 当我们想在某个节点之前或之后插入内容时,
before和after提供了精确的插入点。例如,在处理列表、段落、表格等结构时,通常会用到这些概念。 - 例如,插入一个段落到另一个段落之前,可以使用该段落的
before位置作为目标路径。
- 当我们想在某个节点之前或之后插入内容时,
-
节点移动:
-
before和after也可以用于移动节点。假设要将一个节点移动到文档的某个位置,它可以被移动到目标节点的before或after位置,从而控制新节点的位置。
-
-
选择管理:
- 在管理用户选区时,选区的起点和终点也可以根据
before和after来确定。例如,在光标位于某个位置时,插入文本可以发生在当前选区的after位置,或者在选中的内容before的位置。
- 在管理用户选区时,选区的起点和终点也可以根据
before 和 after 的工作机制
-
path.before():- 此方法用于返回表示该节点前面位置的
path。即通过调整path中最后一个索引减 1,来生成目标节点之前的位置。例如:const path = [2]; // 目标节点的路径 const beforePath = path.before(); // 结果为 [1]
- 此方法用于返回表示该节点前面位置的
-
path.after():- 此方法返回表示该节点后面位置的
path。即通过调整path中最后一个索引加 1,生成目标节点之后的位置。例如:const path = [2]; // 目标节点的路径 const afterPath = path.after(); // 结果为 [3]
- 此方法返回表示该节点后面位置的
这些方法允许你轻松地获取某个节点前面或后面的插入点,尤其是在处理复杂文档结构时,能够帮助开发者精确定位操作位置。
实际代码示例
假设我们有一个段落和需要在它之前或之后插入一个新的段落,我们可以这样使用 before 和 after:
const root = editor.model.document.getRoot(); // 获取模型的根节点
const paragraphPath = [0]; // 假设我们定位到第一个段落
// 获取 before 和 after 的路径
const beforeParagraphPath = paragraphPath.before(); // 在段落之前
const afterParagraphPath = paragraphPath.after(); // 在段落之后
// 插入新段落到 before 位置
editor.model.change( writer => {
const newParagraph = writer.createElement( 'paragraph' );
writer.insert( newParagraph, root, beforeParagraphPath );
});
// 插入新段落到 after 位置
editor.model.change( writer => {
const newParagraph = writer.createElement( 'paragraph' );
writer.insert( newParagraph, root, afterParagraphPath );
});
before和after的总结
-
before:表示当前节点的前一个位置,通常用于在该节点之前插入或操作。 -
after:表示当前节点的后一个位置,通常用于在该节点之后插入或操作。
这些概念在 @ckeditor/ckeditor5-engine 中非常有用,尤其是在处理复杂的文档编辑、插入节点和移动节点时,可以确保操作精确、可靠。