本库包含用于 WordPress 文档的默认块序列化解析器实现。它提供了原生 PHP 和 JavaScript 解析器,这些解析器实现了 @wordpress/block-serialization-spec-parser 中的规范,并且通常对存储在 post_content 中的文档进行操作。

安装

安装模块

npm install @wordpress/block-serialization-default-parser --save

此包假设您的代码将在 ES2015+ 环境中运行。如果您使用的是对这类语言特性和 API 支持有限或完全不支持的環境,您应该在代码中包含 @wordpress/babel-preset-default 中提供的 polyfill。

API

parse

解析函数,将输入的 HTML 转换为基于块的结构。

用法

输入文章:

<!-- wp:columns {"columns":3} -->
<div class="wp-block-columns has-3-columns">
    <!-- wp:column -->
    <div class="wp-block-column">
        <!-- wp:paragraph -->
        <p>Left</p>
        <!-- /wp:paragraph -->
    </div>
    <!-- /wp:column -->

    <!-- wp:column -->
    <div class="wp-block-column">
        <!-- wp:paragraph -->
        <p><strong>Middle</strong></p>
        <!-- /wp:paragraph -->
    </div>
    <!-- /wp:column -->

    <!-- wp:column -->
    <div class="wp-block-column"></div>
    <!-- /wp:column -->
</div>
<!-- /wp:columns -->

解析代码:

import { parse } from '@wordpress/block-serialization-default-parser';

parse( post ) ===
    [
        {
            blockName: 'core/columns',
            attrs: {
                columns: 3,
            },
            innerBlocks: [
                {
                    blockName: 'core/column',
                    attrs: null,
                    innerBlocks: [
                        {
                            blockName: 'core/paragraph',
                            attrs: null,
                            innerBlocks: [],
                            innerHTML: '\n<p>Left</p>\n',
                        },
                    ],
                    innerHTML: '\n<div class="wp-block-column"></div>\n',
                },
                {
                    blockName: 'core/column',
                    attrs: null,
                    innerBlocks: [
                        {
                            blockName: 'core/paragraph',
                            attrs: null,
                            innerBlocks: [],
                            innerHTML: '\n<p><strong>Middle</strong></p>\n',
                        },
                    ],
                    innerHTML: '\n<div class="wp-block-column"></div>\n',
                },
                {
                    blockName: 'core/column',
                    attrs: null,
                    innerBlocks: [],
                    innerHTML: '\n<div class="wp-block-column"></div>\n',
                },
            ],
            innerHTML:
                '\n<div class="wp-block-columns has-3-columns">\n\n\n\n</div>\n',
        },
    ];

参数

  • doc string: 要解析的 HTML 文档。

返回

  • ParsedBlock[]: 输入 HTML 的基于块的表示形式。

理论

它与 spec-parser 有什么不同?

这是一个递归下降解析器,它只扫描一次输入文档。它不使用直接递归,而是利用 trampolin(尾调用优化)机制来防止栈溢出。它通过全局变量在解析过程中跟踪状态,从而最大限度地减少数据复制和传递。在每个标记(即块注释分隔符)之间,我们可以对解析器进行插桩并干预,例如,我们可以设置解析文档的最大长度限制,或者为文档提供额外的调试诊断信息。

规范解析器是通过 解析表达式语法 (PEG) 定义的,它隐式地回答了我们在本解析器中必须显式回答的许多问题。此实现的目的是匹配 PEG 的特性,以便可以直接替换,唯一的改进是更好的运行时性能和内存使用。

它是如何工作的?

每个序列化的 Gutenberg 文档名义上是一个 HTML 文档,除了正常的 HTML 之外,还可能包含专门设计的 HTML 注释——块注释分隔符——它们分离并隔离文档中序列化的块。

此解析器试图围绕由这些分隔符触发的转换创建一个状态机——语法的“标记”。每次我们找到一个时,我们应该只做以下两种操作之一:

  • 进入一个新块;
  • 退出一个块。

这些操作的效果取决于上下文;例如,当我们退出一个块时,我们需要将其添加到输出块列表中 或者 将其作为下一个 innerBlock 附加到其下方的父块上(我们跟踪打开块的地方)。详细信息记录如下。

此解析器的最大挑战是在每一层嵌套深度下,正确计算构建每个块的 innerHTML 值所需的索引。我们采取一种简单的方法:

  • 为新打开的每个块启动空的 innerHTML。
  • 每当我们将第一个块推入 innerBlocks 列表时,添加从父块内容开始到此内部块开始处的内容。
  • 每当我们将另一个块推入 innerBlocks 列表时,添加从前一个内部块结束处到此内部块开始处的内容。
  • 当我们关闭一个打开的块时,添加从最后一个内部块结束处到关闭块分隔符开始处的内容。
  • 如果没有内部块,则将打开和关闭块注释分隔符之间的全部内容作为 innerHTML。

我的意思是,它的性能如何?

此解析器的运行速度比从规范生成的解析器快得多。因为我们比 PEG 更了解解析过程,所以我们可以利用多种技巧来提高速度和内存使用效率:

  • 根据观察方式的不同,我们只有一个或两个不同的标记,并且它们都可以通过正则表达式轻松匹配。与其逐个字符地进行解析,不如允许 PCRE RegExp 引擎为我们跳过文档的大量部分以找到这些标记。
  • 由于 preg_match() 接受 offset 参数,我们可以在不每一步都传递输入文本副本的情况下遍历输入。我们可以跟踪字符串中的位置,只传递数字。
  • 不复制所有这些字符串意味着我们还将跳过许多内存分配。

此外,使用 RegExp 进行标记化带来了额外的优势。PEG 生成的解析器以牺牲对标记化规则的控制为代价提供可预测的性能特性——它不允许我们在规则中定义 RegExp 模式,以防止例如会破坏 PEG 保证的灾难性回溯。

然而,由于我们的块注释分隔符“标记语言”是 规则的 并且 可以 用 RegExp 模式轻松匹配,我们可以在这里这样做,然后发生一些神奇的事情:我们跳出 PHP 或 JavaScript,进入主机系统上用 C 或 C++ 编写的高度优化的 RegExp 引擎。因此我们离开了虚拟机及其开销。

为此包做出贡献

这是 Gutenberg 项目的一部分单个包。该项目组织为一个 monorepo。它由多个自包含的软件包组成,每个包都有特定的用途。此 monorepo 中的包发布到 npm,并被 WordPress 以及其他软件项目使用。

要了解有关为此包或整个 Gutenberg 做出贡献的更多信息,请阅读项目的主要 贡献者指南。