【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 的时候就是这么干的, 不过你可以覆盖它.

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

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 连接查询和子查询哪个效率高

    连接查询和子查询哪个效率高需要进行多表查询的情况下,用连接查询和子查询哪个效率高?1、什么是子查询?举个简单的例子,那么子查询有什么优劣呢?子查询(内查询)在主查询之前一次执行完成。子查询的结果被主查询(外查询)使用。可以用一个子查询替代上边的的表名。子查询,将查询操作嵌套在另一个查询操作中。先执行子查询,再执行外查询注:在查询时基于未知的值时,应使用子查询子查询可以返回多个结果/单个结果,结果个数不同应该使用不同的操作符通过子查询不难看出,可以根据employee_id查到department_

  • 微型计算机的档次主要取决于,微型计算机的性能主要取决于

    微型计算机的档次主要取决于,微型计算机的性能主要取决于匿名用户1级2013-07-10回答“微型计算机的性能主要取决于什么?”主要看三大件,CPU,主板,内存。1、CPU:其功能主要是解释计算机指令以及处理计算机软件中的数据,他的速度快慢可以代表计算机处理数据的能力的高低。2、内存:它是与CPU进行沟通的桥梁,计算机中所有程序的运行都是在内存中进行的,因此内存的性能对计算机的影响非常大。3、主板:主板在整个微机系统中扮演着举足轻重的角色。主板的类型…

  • 杭电 HDU ACM 1698 Just a Hook(线段树 区间更新 延迟标记)

    杭电 HDU ACM 1698 Just a Hook(线段树 区间更新 延迟标记)

  • Python3取余不等于0(巨坑)[通俗易懂]

    Python3取余不等于0(巨坑)[通俗易懂]这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML图表FLowchart流程图导出与导入导出导入欢迎使用Markdown编辑器你好!这是你第一次使用Markdown编辑器所展示的欢迎页。如果你想学习如何使用Mar

  • Decorator 单一职责模式[通俗易懂]

    Decorator 单一职责模式[通俗易懂]单一职责模式动机模式定义案例结构要点总结笔记动机在某些情况下我们可能会“过度地使用继承来扩展对象的功能”,由于继承为类型引入的静态特质,使得这种扩展方式缺乏灵活性;并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多的子类的膨胀如何使“对象功能的扩展”能够根据需要来动态实现?同时避免”扩展功能的增多“带来的子类膨胀问题?从而使得任何任何”功能扩展变化“所导致的影响将为最低?模式定义动态(组合)地给一个对象增加一些额外的职责。就增加功能而言Decorator模式比生成子类

发表回复

您的电子邮箱地址不会被公开。

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