【ProseMirror学习笔记 3 】—— schema

【ProseMirror学习笔记 3 】—— schemaschemaschema介绍nodetypesContentExpressionstodogroupMarksAttributesSerializationandParsing   schema介绍每个Prosemirrordocument都有一个与之相关的schema.这个schema描述了document中的的nodes类型,和nodes们的嵌套关系.例如,schema可以规定,顶级节点可以包含一个或者更多的blo

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

 
 
 

schema介绍

  • 每个 Prosemirror document 都有一个与之相关的 schema. 这个 schema 描述了 document 中的的nodes 类型, 和 nodes 们的嵌套关系. 例如, schema 可以规定, 顶级节点可以包含一个或者更多的 blocks, 同时段落 paragraph nodes 可以包含含有任意数量的 inline nodes, 这些 inline nodes 可以含有任意数量的 marks.

  • 一个document schema,有nodes和marks对象,之后nodes和marks都会遵从这个对象的约束。并提供创建和反序列化此类文档的功能

nodes: Object<NodeSpec> | OrderedMap<NodeSpec>

将名称映射到NodeSpec对象。它们的顺序很重要,它决定了默认情况下哪些解析规则优先,以及给定组中哪些节点排在第一位。

marks: ?⁠Object<MarkSpec> | OrderedMap<MarkSpec>

它们的顺序决定了marks的排序顺序和尝试解析规则的顺序。

  • new Schema(spec: SchemaSpec)
    可以通过给定schemaSpec自定义schema.

    注意:spec 是 specification缩写 ,是规格、说明书的意思;

 
 
 

node types

在 document 中的每个节点都有一个 type, 它代表了一个 node 的语义化上意思和 node 的属性, 这些属性包括在编辑器中的渲染方式.

当你定义一个 schema 的时候, 你需要列举每一个用到的 node types, 用一个 nodespec描述它们:

const trivialSchema = new Schema({ 
   
  nodes: { 
   
    doc: { 
   content: "paragraph+"},
    paragraph: { 
   content: "text*"},
    text: { 
   inline: true},
    /* ... and so on */
  }
})

上述代码定义了一个允许 document 包含一个或更多 paragraphs 的 schema, 每个 paragraph 又能包含任意数量的 text.

每个 schema 至少得定义顶级 node 的 type(顶级 node 的名字默认是 “doc”, 不过你可以配置它), 和规定 text content 的 “text” type.

作为 inline 类型来计算 index 等的 nodes 必须声明它的 inline 属性( text 类型, 就被定义成 inline 了)

 
 
 

Content Expressions

上面 schema 示例代码中的 content 字段的字符串值被叫做 ‘content expressions’. 他们控制着对于当前 type 的 node 来说, 它的children可以有哪些 nodes 类型.

比如说, (content 字段的内容是)”paragraph” 意思就是 “一个 paragraph”, “paragraph+” 意思就是 “一个或者更多 paragraph”.与此相似, “paragraph*” 意思就是 “0 个或者更多 paragraph”, “caption?” 意思就是 “0 个或者 1 个 caption node”. 你也可以在 node 名字之后使用类似于正则表达式中表示范围含义的表达式, 比如 {2}(正好 2 个), {1, 5}(1 个到 5 个), 或者{2, }(两个或更多).

这种表达式可以被联合起来创建一个系列, 例如 “heading paragraph+” 表示 “开头一个 heading, 之后一个或更多 paragraphs”. 你也可以使用管道符号 “|” 操作符来表示在两个表达式中选择一个, 比如 “(paragraph | blockquote)+”.

一些元素 type 的 group 可能在你的 schema 会出现多次, 比如你有一个 “block” 概念的 nodes, 他们可以出现在顶级元素之下, 也可以嵌套进 blockquote 类型的 node 内. 你可以通过指定 schema 的 group 属性来创建一个 node group, 然后在你的其他表达式中填 group 的名字即可:

const groupSchema = new Schema({ 
   
  nodes: { 
   
    doc: { 
   content: "block+"},
    paragraph: { 
   group: "block", content: "text*"},
    blockquote: { 
   group: "block", content: "block+"},
    text: { 
   }
  }
})

上面示例中, “block+” 等价于 “(paragraph | blockquote)+”.

todo group

建议在允许 block content 的 nodes(在示例中就是 doc 和 blockquote)中设置为至少有一个 child node, 因为如果 node 为空的话浏览器将折叠它, 使它无法编辑(这句话的意思是, 如果上述 doc 或者 blockquote 的 content 设置为 block* 而不是 block+ 就表示允许不存在 child nodes 存在的情况(它沿用了通用的正则符号: * 表示0个或更多, + 表示1个或更多), 那么此时编辑的话浏览器输入的是 text node, 是 inline 节点, 导致无法输入, 读者可以试试).

在 schema 中, nodes 的书写顺序很重要. 当对一个必选的 node 新建一个默认实例的时候, 比如在应用了一个 replace step 之后, 为了保持当前文档仍然符合 schema 的约束, 会使用能满足 schema 约束的第一个 node 的 expression. 如果 node 的 expression 是一个 group, 则这个 group 的第一个 node type(决定于当前 group 的成员 node 出现在 schema 的顺序)将被使用. 如果我在上述的 schema 示例中调换了 “paragraph” 和 “blockquote” 的顺序, 当编辑器试图新建一个 block node 的时候将会报 stack overflow——因为编辑器会首先尝试新建一个 “blockquote” node, 但是这个 node 需要至少一个 block node, 于是它就首先又需要创建一个 “blockquote” node 作为内容, 以此往复.

不是每个 Prosemirror 库中的 node 操作函数都会检查它当前处理 content 的可用性——高级概念例如 transforms 会检查, 但是底层的 node 新建方法通常不会, 这些底层方法通常将可用性检查交给它们的调用者. 它们(即使当前操作的 content 不可用, 但是这些底层方法也)完全可能可用, 比如, NodeType.create, 它会创建一个含有不可用 content 的节点. 对于在一个 slices 的 “open” 一边的 node 而言, 这甚至是情有可原的(因为 slice 不是一个可用的节点, 但是又需要直接操作 slice ——总不能让用户手动补全吧?——译者注). 有一个 createChecked 方法可以检查给定 content 是否符合 schema, 也有一个 check 方法来 assert 给定的 content 是否可用.

 
 
 

Marks

Marks 通常被用来对 inline content 增加额外的样式和其他信息. schema 必须声明当前 document 允许的所有 schema(就像声明 nodes 那样).

Mark types 是一个有点像 node types 的对象, 它用来给不同的 mark 分类和提供额外的信息.

默认情况下, 有 inline content 的 nodes 允许所有的定义在 schema 的 marks 应用于它的 child nodes.

下面是一个简单的 schema 示例, 支持在 paragraphs 中设置文本的 strong 和 emphasis marks, 不过 heading 则不允许设置这两种 marks.

const markSchema = new Schema({ 
   
  nodes: { 
   
    doc: { 
   content: "block+"},
    paragraph: { 
   group: "block", content: "text*", marks: "_"},
    heading: { 
   group: "block", content: "text*", marks: ""},
    text: { 
   inline: true}
  },
  marks: { 
   
    strong: { 
   },
    em: { 
   }
  }
})

marks 字段的值可以写成用逗号分隔开的 marks 名字, 或者 mark groups——
”_”, 它是通配符的意思, 允许所有的 marks.;
空字符串“” 表示不允许任何 marks.

 
 
 

Attributes

Document 的 schema 也定义了 node 和 mark 允许有哪些 attributes. 如果你的 node type 需要额外的 node 专属的信息, 比如 heading node 的 level 信息(H1, H2等等), 此时适合使用 attribute.

Attribute 是一个普通的纯对象, 它有一些预先定义好的(在每个 node 或 mark 上)属性, 指向可以被 JSON 序列化的值. 为了指定哪些 attributes 被允许出现, 可以在 node spec 和 mark 的 spec 中使用可选的 attr 属性:

heading: { 
   
  content: "text*",
  attrs: { 
   level: { 
   default: 1}}
}

在上面这个 schema 中, 每个 heading node 实例都有一个 level 属性通过 .attrs.level 访问. 如果在新建 heading 的时候没有指定, level 默认是 1.

如果你在定义 node 的时候没有给一个 attribute 默认值的话, 当新建这个 node 的时候, 如果没有显式传入 attribute 就会报错. 这也让 Prosemirror 在调用一些接口如 createAndFill 来生成满足 schema 约束的 node 的时候变得不可能.

 
 
 

Serialization and Parsing

为了能在浏览器中编辑元素, 就必须使 document nodes 以 DOM 的形式展示出来. 最简单的方式就是在 schema 中对每个 node 注明如何在 DOM 中显示. 这可以在 schema 的每个 node spec 中指定 toDOM 字段来实现.

这个字段应该指向一个函数, 这个函数将当前 node 作为参数, 返回 node 的 DOM 结构描述. 这可以直接是一个 DOM node, 或者一个 array 来描述, 例如:

const schema = new Schema({ 
   
  nodes: { 
   
    doc: { 
   content: "paragraph+"},
    paragraph: { 
   
      content: "text*",
      toDOM(node) { 
    return ["p", 0] }
    },
    text: { 
   }
  }
})

上面示例中, [“p”, 0] 的含义是 paragraph 节点在 HTML 中被渲染成

标签. 0 代表一个 “hole”, 表示该 node 的内容应该被渲染的地方(意思就是如果这个节点预期是有内容的, 就应该在数组最后写上 0). 你也可以在标签后面加上一个对象表示 HTML 的 attributes, 例如 [“div”, {class: “c”}, 0]. leaf nodes 不需要 “hole” 在它们的 DOM 中, 因为他们没有内容.

  • 上面示例中, [“p”, 0] 的含义是 paragraph 节点在 HTML 中被渲染成 < p> 标签.
  • 0 代表一个 “hole”, 表示该 node 的内容应该被渲染的地方(意思就是如果这个节点预期是有内容的, 就应该在数组最后写上 0). 你也可以在标签后面加上一个对象表示 HTML 的 attributes, 例如 [“div”, {class: “c”}, 0]…
  • 叶子结点 不需要 “hole” 在它们的 DOM 中, 因为他们没有内容.

 

 
Mark 的 specs 有一个跟 nodes 相似的 toDOM 方法, 不同的是他们需要渲染成单独的标签去直接包裹着 content, 所以这些 content 直接在返回的 node 中, 上面的 “hole” 就不用专门指定了.

当用户粘贴或者拖拽东西到编辑器中的时候. Prosemirror-model 模块的函数可以来处理这些事情, 不过你也可以在 schema 中的 parseDOM 属性中直接描述如何格式化信息.

这里列出了一组格式化的规则, 描述了 DOM 如何映射成 node 或者 mark. 例如, 基本的 schema 对于 emphasis mark 写成下面这样:

parseDOM: [
  { 
   tag: "em"},                 // Match <em> nodes
  { 
   tag: "i"},                  // and <i> nodes
  { 
   style: "font-style=italic"} // and inline 'font-style: italic'
]

上面中的 tag 字段也可以是一个 CSS 选择器, 比如 “div.myclass” 这种字符串.。与此相似, style 字段也可以匹配行内 CSS 样式.

当一个 schema 包含 parseDOM 字段时, 你可以使用 DOMParser.fromSchema 创建一个 DOMParser 对象. 编辑器在新建默认的剪切板内容 parser 的时候就是这么干的, 不过你可以覆盖它.

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/194680.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • Linux下ffmpeg安装教程(亲测有效)「建议收藏」

    Linux下ffmpeg安装教程(亲测有效)「建议收藏」Linux下ffmpeg安装教程(亲测有效)

    2025年11月8日
    2
  • 易文档-快速编写专业漂亮的API文档,产品文档,使用手册

    易文档https://easydoc.top让您轻松编写和维护高质量的文档。从需求文档、API文档、部署文档到使用手册,多种定制文档编辑器,满足您整个开发周期需求;支持接口在线测试,一键生成mock配置。极致的编写体验,优雅的排版,让文档成为一种乐趣。查看示例文档查看使用技巧…

    2022年4月5日
    344
  • 朋友圈集赞神器!再也不怕谁让集赞了

    朋友圈集赞神器!再也不怕谁让集赞了这里是「每周分享」的第34期。往期分享内容可以在公众号后台的「不务正业」菜单中找到,Python和机器学习类的文章在另一个「不误正业」菜单中。这一期的话题是:朋友…

    2025年9月17日
    6
  • 小学计算机画线反思,小学四年级数学上册《画平行线》教学反思[通俗易懂]

    小学计算机画线反思,小学四年级数学上册《画平行线》教学反思[通俗易懂]小学四年级数学上册《画平行线》教学反思过去,对于平行线的画法,我也感到很不理解,特别是用尺子移来移去,实在太麻烦,对于平行线的理解,学生只知道“在同一平面内不相交的两条直线是平行线”,而不相交的实质是“两条直线间的距离是固定的.”学生并没有直观感受。正是基于这样的认识画平行线的教学只能由教师传授给学生,他们也只能是机械的模仿,也就是简单的完成操作工的活动,没有任何思维的含量,不能算真正意义上的脑力…

    2025年12月10日
    5
  • 视频传输协议(常用的视频协议)

    SDP协议RTPRTCPSRTPRTP只负责传输数据包,需要与RTCP配合使用,由RTCP来保证RTP数据包的服务质量。每一个RTP数据报都由头部(Header)和负载(Payload)两个部分组成,其中头部前12个字节的含义是固定的,而负载则可以是音频或者视频数据。整个IP报文由IP报头、UDP报头、RTP报头、RTPPayload(音频或视频数据)组成, IP协议最大传输单元(MTU)最大为1500字节,其中包括至少20字节的IP头、8字节的UDP头、12字节的RTP头, 这样,头信息

    2022年4月17日
    106
  • 什么是高内聚、低耦合?

    什么是高内聚、低耦合?

    2021年10月10日
    50

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号