ProseMirror Schema Basic 模块

prosemirror

ProseMirror Schema Basic 模块

prosemirror-schema-basic 模块提供了一个 schema 数据结构的约束对象,以及它的配置对象(符合一种 TypeScript interface SchemaSpec)的属性 nodes 和属性 marks 的值(分别表示该文档所支持的节点类型和样式标记类型)

schema 对象

该模块导出一个 schema 数据结构的约束对象,它支持的节点类型和样式标记类型参考 CommonMark(一种 Markdown 格式)

ts
schema: Schema<
  // 该 schema 支持多种 nodes 节点类型
  "blockquote" |
  "doc" |
  "paragraph" |
  "horizontal_rule" |
  "heading" |
  "code_block" |
  "text" |
  "image" |
  "hard_break"
  ,
  // 也支持一些 marks 样式标记类型
  "code" | "em" | "strong" | "link"
>
注意

schema 并不包含列表节点,可以查看官方所提供的另一个模块 prosemirror-schema-list

提示

可以直接使用该 schema 对象来创建编辑器,也可以基于它进行扩展,定制出符合自己编辑器使用场景的数据结构

具体支持哪些节点和样式标记可以通过该对象的属性 schema.spec.nodesschema.spec.marks 来查看

nodes 配置对象

该模块导出了一个 nodes 对象,它包含一些属性(其值需要符合一种 TypeScript interface NodeSpec,规定了节点可接受哪些配置参数)对不同类型的节点进行描述

如何使用

在使用方法 new Schema(spec) 实例化 Schema 类时,该对象作为配置参数 spec(它是一个对象,符合一种 TypeScript interface SchemaSpec)的属性 spec.nodes 的值

ts
export const nodes = {
  // 文档的根节点
  doc: {
    // 其内容(子节点)需要是 block group 中所包含的节点
    // 具体包括 paragraph、blockquote、horizontal_rule、heading、code_block 这 5 种类型的节点
    // 而数量是一个或多个
    content: "block+"
  } as NodeSpec,
  // 文本节点
  text: {
    // 将它归到 inline group 组别中
    group: "inline"
  } as NodeSpec,
  // 段落节点
  paragraph: {
    // 其内容(子节点)需要是 inline group 组别中所包含的节点
    // 具体包括 text、image、hard_break 这 3 种类型的节点
    // 而数量是零个或多个
    content: "inline*",
    // 将它归到 block group 组别中
    group: "block",
    // 定义了如何将 DOM 元素解析为该类型节点的一些规则
    // 这里只设置了一条规则
    parseDOM: [
      // 基于 DOM 元素的名称进行匹配,即将 HTML 中的 `<p>` 元素解析为段落节点
      {tag: "p"}
    ],
    // 定义了如何将该类型的节点转换为相应的 DOM 元素
    toDOM() { return pDOM }
  } as NodeSpec,
  // 引文节点
  blockquote: {
    // 其内容(子节点)需要是 block group 中所包含的节点
    // 具体包括 paragraph、blockquote、horizontal_rule、heading、code_block 这 5 种类型的节点,它可以使用自身类型的节点作为子节点
    // 而数量是一个或多个
    content: "block+",
    // 将它归到 block group 组别中
    group: "block",
    // 表示该类型的节点是否具有特殊的上下文语义,用于控制与该节点相关的复制粘贴行为
    // 如果设置为 true 时会同时将该节点的 definingAsContext 属性和 definingForContent 属性都设置为 true
    defining: true,
    // 定义了如何将 DOM 元素解析为该类型节点的一些规则
    // 这里只设置了一条规则
    parseDOM: [
      // 基于 DOM 元素的名称进行匹配,即将 HTML 中的 `<blockquote>` 元素解析为引文节点
      {tag: "blockquote"}
    ],
    // 定义了如何将该类型的节点转换为相应的 DOM 元素
    toDOM() { return blockquoteDOM }
  } as NodeSpec,
  // 水平线节点
  horizontal_rule: {
    // 将它归到 block group 组别中
    group: "block",
    // 定义了如何将 DOM 元素解析为该类型节点的一些规则
    // 这里只设置了一条规则
    parseDOM: [
      // 基于 DOM 元素的名称进行匹配,即将 HTML 中的 `<hr>` 元素解析为水平线节点
      {tag: "hr"}
    ],
    // 定义了如何将该类型的节点转换为相应的 DOM 元素
    toDOM() { return hrDOM }
  } as NodeSpec,
  // 标题节点
  heading: {
    // 设置该类型的节点所拥有的 attributes 以添加额外的信息
    attrs: {
      // 属性 level 表示标题节点的层级(可以是数字 1 至 6 任一值,分别对应 DOM 元素 <h1> 到 <h6>)
      // 默认值是 1(即实例化标题节点时,如果省略配置 level 属性,则默认创建一个 1 级标题节点)
      level: {default: 1}
    },
    // 其内容(子节点)需要是 inline group 组别中所包含的节点(具体包括 text、image、hard_break 这 3 种类型的节点)
    // 而数量是零个或多个
    content: "inline*",
    // 将它归到 block group 组别中
    group: "block",
    // 表示该类型的节点是否具有特殊的上下文语义,用于控制与该节点相关的复制粘贴行为
    // 如果设置为 true 时会同时将该节点的 definingAsContext 属性和 definingForContent 属性都设置为 true
    defining: true,
    // 定义了如何将 DOM 元素解析为该类型节点的一些规则
    // 这里设置了 6 条规则,分别对应将 <h1> 到 <h6> 元素转换为标题节点(需要为节点的属性 attrs.level 设置对应的值)
    parseDOM: [
      {tag: "h1", attrs: {level: 1}},
      {tag: "h2", attrs: {level: 2}},
      {tag: "h3", attrs: {level: 3}},
      {tag: "h4", attrs: {level: 4}},
      {tag: "h5", attrs: {level: 5}},
      {tag: "h6", attrs: {level: 6}}
    ],
    // 定义了如何将该类型的节点转换为相应的 DOM 元素
    // 根据节点的属性 attrs.level 合成 DOM 元素的名称
    toDOM(node) { return ["h" + node.attrs.level, 0] }
  } as NodeSpec,
  // 代码块节点
  // Represented as a `<pre>` element with a
  /// `<code>` element inside of it.
  code_block: {
    // 其内容只允许文本节点
    // 而数量是零个或多个
    content: "text*",
    // 而且不允许任何类型的 marks 样式标记对文本进行修饰
    marks: "",
    // 将它归到 block group 组别中
    group: "block",
    // 表示该节点内包含代码文字
    // 以告诉 ProseMirror 采用不同的处理方式
    // 例如粘贴内容到节点内时,格式化的方式不同,需要保留空格和缩进等
    code: true,
    // 表示该类型的节点是否具有特殊的上下文语义,用于控制与该节点相关的复制粘贴行为
    // 如果设置为 true 时会同时将该节点的 definingAsContext 属性和 definingForContent 属性都设置为 true
    defining: true,
    // 定义了如何将 DOM 元素解析为该类型节点的一些规则
    // 这里只设置了一条规则
    parseDOM: [
      // 基于 DOM 元素的名称进行匹配,即将 HTML 中的 `<pre>` 元素解析为代码块节点
      // 而且配置属性 preserveWhitespace 为 `full` 表示完全保留空格键和换行符
      {tag: "pre", preserveWhitespace: "full"}
    ],
    // 定义了如何将该类型的节点转换为相应的 DOM 元素
    toDOM() { return preDOM }
  } as NodeSpec,
  // 图像节点
  image: {
    // 表示该节点为行内类型
    inline: true,
    // 设置该类型的节点所拥有的 attributes 以添加额外的信息
    attrs: {
      // 属性 src 是图片的路径
      src: {},
      // 属性 alt 是对图像的文本描述,默认为空
      alt: {default: null},
      // 属性 title 是作为 tooltip 提示文本呈现给用户,默认为空
      title: {default: null}
    },
    // 将它归到 inline group 组别中
    group: "inline",
    // 表示该节点(在非选中的情况下)允许拖拽移动。
    draggable: true,
    // 定义了如何将 DOM 元素解析为该类型节点的一些规则
    // 这里只设置了一条规则
    parseDOM: [
      // 基于 DOM 元素的名称进行匹配,即将 HTML 中的 `<img>` 元素(需要具有 src 属性)解析为图像节点
      {
        tag: "img[src]",
        // 使用 getAttrs 属性更精细地设置 attributes
        // 💡 也有些节点(不是当前示例)使用该属性(基于特定的 attributes)匹配 DOM 元素,如果该函数返回 false,表示该 DOM 元素无法满足规则,即无法匹配
        getAttrs(dom: HTMLElement) {
          // 从 DOM 元素的相应属性获取值,以设置节点的一系列 attributes
          return {
            src: dom.getAttribute("src"),
            title: dom.getAttribute("title"),
            alt: dom.getAttribute("alt")
          }
        }
      }
    ],
    // 定义了如何将该类型的节点转换为相应的 DOM 元素
    toDOM(node) { let {src, alt, title} = node.attrs; return ["img", {src, alt, title}] }
  } as NodeSpec,
  // 强制换行节点
  hard_break: {
    // 表示该节点为行内类型
    inline: true,
    // 将它归到 inline group 组别中
    group: "inline",
    // 表示该节点不允许被选中
    selectable: false,
    // 定义了如何将 DOM 元素解析为该类型节点的一些规则
    // 这里只设置了一条规则
    parseDOM: [
      // 基于 DOM 元素的名称进行匹配,即将 HTML 中的 `<br>` 元素解析为强制换行节点
      {tag: "br"}
    ],
    // 定义了如何将该类型的节点转换为相应的 DOM 元素
    toDOM() { return brDOM }
  } as NodeSpec
}

在以上代码中涉及到一些变量(可以是一个字符串、一个 DOM 元素、一个对象,或一个数组),它们作为 toDOM 方法的返回值(符合 TypeScript type DOMOutputSpec),它们描述了如何将节点转换为 DOM 元素

ts
const pDOM: DOMOutputSpec = ["p", 0], // pDOM 描述了段落节点如何转换为 DOM 元素
      blockquoteDOM: DOMOutputSpec = ["blockquote", 0], // blockquoteDOM 描述了引文节点如何转换为 DOM 元素
      hrDOM: DOMOutputSpec = ["hr"], // hrDOM 描述了将水平线节点如何转换为 DOM 元素
      // preDOM 描述了代码块节点如何转换为 DOM 元素
      // 转换为 <pre><code>content</code></pre> 的形式
      preDOM: DOMOutputSpec = ["pre", ["code", 0]],
      brDOM: DOMOutputSpec = ["br"] // brDOM 描述了强制换行节点如何转换为 DOM 元素

marks 配置对象

该模块导出了一个 marks 对象,它包含一些属性(其值需要符合一种 TypeScript interface MarkSpec,规定了样式标记可接受哪些配置参数)对不同类型的样式标记进行描述

如何使用

在使用方法 new Schema(spec) 实例化 Schema 类时,该对象作为配置参数 spec(它是一个对象,符合一种 TypeScript interface SchemaSpec)的属性 spec.marks 的值

ts
export const marks = {
  // 链接样式标记 Has `href` and `title` attributes. `title`
  /// defaults to the empty string. Rendered and parsed as an `<a>`
  /// element.
  link: {
    // 设置该类型的节点所拥有的 attributes 以添加额外的信息
    attrs: {
      // 属性 href 是链接所指向的 URL
      href: {},
      // 属性 title 是作为 tooltip 提示文本呈现给用户,默认为空
      title: {default: null}
    },
    // 表示光标位于该样式标记结尾处,该样式标记是否需要 active 激活
    // 设置为 false 时,表示用户继续输入文字不带有同样的样式标记
    // 💡 如果光标置于样式标记的开头时,而(该样式标记所应用的)节点同时位于其父节点的开头,则是否激活也同样受该属性控制
    inclusive: false,
    // 定义了如何将 DOM 元素解析为该类型的样式标记的一些规则
    // 这里只设置了一条规则
    parseDOM: [
      {
        // 基于 DOM 元素的名称进行匹配,即将 HTML 中的 `<a>` 元素(需要具有 href 属性)解析为链接样式标记
        tag: "a[href]",
        // 使用 getAttrs 属性更精细地设置 attributes
        getAttrs(dom: HTMLElement) {
          // 从 DOM 元素的相应属性获取值,以设置该样式标记的一系列 attributes
          return {
            href: dom.getAttribute("href"),
            title: dom.getAttribute("title")
          }
        }
      }
    ],
    // 定义了如何将该类型的样式标记转换为相应的 DOM 元素
    toDOM(node) { let {href, title} = node.attrs; return ["a", {href, title}, 0] }
  } as MarkSpec,
  // 斜体式强调样式标记
  em: {
    // 定义了如何将 DOM 元素解析为该类型的样式标记的一些规则
    // 这里设置了 4 条规则
    parseDOM: [
      // 基于 DOM 元素的名称进行匹配,即将 HTML 中的 `<i>` 元素解析为斜体式强调样式标记
      {tag: "i"},
      // 基于 DOM 元素的名称进行匹配,即将 HTML 中的 `<em>` 元素解析为斜体式强调样式标记
      {tag: "em"},
      // 基于 inline style 行内样式进行匹配,即将带有内联样式 "font-style=italic" 的 HTML 元素解析为斜体式强调样式标记
      {style: "font-style=italic"},
      // 基于 inline style 行内样式进行匹配,即带有内联样式 "font-style=normal" 的 HTML 元素
      // 这里设置了 clearMark 属性,所以该匹配规则并不是为了将 HTML 元素解析为特定类型的样式标记
      // 而是清除已激活的样式标记(如果该 HTML 之前有匹配到其他规则,并激活了名为 `em` 的样式标记,在该规则要将它清除)
      {style: "font-style=normal", clearMark: m => m.type.name == "em"}
    ],
    // 定义了如何将该类型的样式标记转换为相应的 DOM 元素
    toDOM() { return emDOM }
  } as MarkSpec,
  // 加粗式强调样式标记
  strong: {
    // 定义了如何将 DOM 元素解析为该类型的样式标记的一些规则
    // 这里设置了 4 条规则
    parseDOM: [
      // 基于 DOM 元素的名称进行匹配,即将 HTML 中的 `<strong>` 元素解析为加粗式强调样式标记
      {tag: "strong"},
      // 该解析规则为了处理 Google Docs 的奇异行为所造成的问题
      // 从 Google Docs 复制内容时,会使用 <b>(而不是 <span> 或 <div>)作为一个容器(对文本内容进行包裹,并添加一个 id 属性),并添加 font-weight="normal"(以区分真正带有加粗语义的 `<b>` 标签)
      // 所以以下规则在基于 HTML 标签名称进行匹配后,还采用属性 getAttrs 进行更精细的判断,避免错误地将作为容器的 `<b>` 标签解析为加粗
      // 当所匹配的 <b> 标签同时具有内联样式 font-weight="normal" 则该函数返回 false(即 getAttrs 属性为 false)表示匹配失败
      // 如果 <b> 标签不具有内联样式 font-weight="normal",则该函数返回 null,表示匹配成功(但是该 mark 不具有 attrs)
      {
        tag: "b",
        getAttrs: (node: HTMLElement) => node.style.fontWeight != "normal" && null
      },
      // 基于 inline style 行内样式进行匹配,即带有内联样式 "font-weight=400"(字重与 normal 一致)的 HTML 元素
      // 这里设置了 clearMark 属性,所以该匹配规则并不是为了将 HTML 元素解析为特定类型的样式标记
      // 而是清除已激活的样式标记(如果该 HTML 之前有匹配到其他规则,并激活了名为 `strong` 的样式标记,在该规则要将它清除)
      {
        style: "font-weight=400",
        clearMark: m => m.type.name == "strong"
      },
      // 基于 inline style 行内样式进行匹配,即内联样式带有 "font-weight" 的 HTML 元素无法满足规则
      // 只有当 `font-weight` 的值为字符串 `bold` 或 `bolder` 或数值(位于 `5xx` 至 `9xx` 之间)才匹配成功
      {
        style: "font-weight",
        getAttrs: (value: string) => /^(bold(er)?|[5-9]\d{2,})$/.test(value) && null
      },
    ],
    // 定义了如何将该类型的样式标记转换为相应的 DOM 元素
    toDOM() { return strongDOM }
  } as MarkSpec,
  // 代码样式标记
  /// Code font mark. Represented as a `<code>` element.
  code: {
    // 定义了如何将 DOM 元素解析为该类型的样式标记的一些规则
    // 这里只设置了一条规则
    parseDOM: [
      // 基于 DOM 元素的名称进行匹配,即将 HTML 中的 `<code>` 元素解析为代码样式标记
      {tag: "code"}
    ],
    // 定义了如何将该类型的样式标记转换为相应的 DOM 元素
    toDOM() { return codeDOM }
  } as MarkSpec
}

在以上代码中涉及到一些变量(可以是一个字符串、一个 DOM 元素、一个对象,或一个数组),它们作为 toDOM 方法的返回值(符合 TypeScript type DOMOutputSpec),它们描述了如何将样式标记转换为相应的 DOM 元素

ts
const emDOM: DOMOutputSpec = ["em", 0], // emDOM 描述了斜体式强调样式标记如何转换为 DOM 元素
      strongDOM: DOMOutputSpec = ["strong", 0], // strongDOM 描述了加粗式强调样式标记如何转换为 DOM 元素
      codeDOM: DOMOutputSpec = ["code", 0] // codeDOM 描述了代码样式标记如何转换为 DOM 元素

Copyright © 2025 Ben

Theme BlogiNote

Icons from Icônes