【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)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 12306网上购票进行身份核验的步骤「建议收藏」

    12306网上购票进行身份核验的步骤「建议收藏」12306自从开始身份核验后,引起很多争议,而且新的政策出来后总要去研究解读,导致很多人不知如何去做。相信很多人会问“12306注册之后,账号激活了,但是身份有待核验,请问这个需要多长时间呢”?然后很

    2022年8月4日
    3
  • sql优化的几种方式

    sql优化的几种方式一、为什么要对SQL进行优化我们开发项目上线初期,由于业务数据量相对较少,一些SQL的执行效率对程序运行效率的影响不太明显,而开发和运维人员也无法判断SQL对程序的运行效率有多大,故很少针对SQL进行专门的优化,而随着时间的积累,业务数据量的增多,SQL的执行效率对程序的运行效率的影响逐渐增大,此时对SQL的优化就很有必要。二、SQL优化的一些方法1.对查询进行优化,应尽量避免全表扫描…

    2022年6月22日
    22
  • C#遍历Dictionary

    C#遍历Dictionary方法推荐:http://www.cnblogs.com/roucheng/p/3521864.html

    2021年12月25日
    49
  • Ubuntu VMware Tools安装详细过程(非常靠谱)「建议收藏」

    Ubuntu VMware Tools安装详细过程(非常靠谱)「建议收藏」说明:该篇博客是博主一字一码编写的,实属不易,请尊重原创,谢谢大家!一.前言VMwareUbuntu安装以及详细过程:https://blog.csdn.net/qq_41782425/article/details/85119258,需要安装Ubuntu系统的小伙伴可以看看,非常详细哦~为什么博主会单独写一个UbuntuVMwaerTools的安装博客,第一:在Ubuntu系…

    2022年5月9日
    45
  • Java学习之SSM框架整合

    Java学习之SSM框架整合0x00前言前面的学习的Spring、SpringMVC和Mybatis框架基本已经学习完了,但是要使用起来,我们需要把这三大框架给整合起来一起使用。0x01

    2021年12月12日
    40
  • 【转贴】Render RenderContents RenderControl 三个方法的区别

    【转贴】Render RenderContents RenderControl 三个方法的区别每个页面都有一个控件树,代表本页面所拥有的所有子控件,页面控件就是控件树的根,为了生成控件树,页面将建立一个HtmlTextWriter类的实例,在这个实例中封装了相应流,接着页面会把HtmlText

    2022年7月4日
    24

发表回复

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

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