对于我们 CSS-Tricks 来说,今年是古腾堡的一年。事实上,这是我们在去年年底设定的目标。我们比我想象的要更进一步,在块编辑器¹中创作所有新的内容,现在为所有内容启用块编辑器。这意味着当我们打开大多数旧帖子时,我们会看到所有内容都在“经典”块中。它看起来像这样

帖子的整个内容都在单个块中,所以没有完全利用块编辑器。它仍然是“可视化的”,就像块编辑器一样,但它更像是使用 TinyMCE 的旧可视化编辑器。我从未使用过它,因为它以我不喜欢的方式强行扭曲了 HTML。
这是我最担心的问题
将经典块转换为新块与选择经典块并选择“转换为块”选项一样简单。

当我们从“转换为块”选项中告诉它这样做时,块编辑器如何处理对旧内容进行块化?如果它在转换过程中完全搞乱了内容怎么办?我们是否能够切换?
答案:它做得相当不错。 但是……仍然存在问题。不是“错误”,而是我们的旧内容中有自定义 HTML,它不知道如何处理它——更不用说如何将其转换为我们希望它那样转换的块。有一种方法!
基本块转换
这就是“块转换”这个想法的来源。所有(好吧,大多数?)原生块都有“to”和“from”转换。您可能已经熟悉它在 UI 中的表现方式。例如,段落可以转换为“to”引用,反之亦然。这是一张关于这段段落的超级元截图

这些转换不是魔法;它们是显式编码的。当您注册一个块时,您会指定转换。假设您要注册自己的自定义代码块。您需要确保您可以对其进行转换……
- From 到默认的内置代码块,以及可能还有其他一些可能有用的块。
- 返回到内置代码块。
它可能看起来像
registerBlockType("my/code-block", {
title: __("My Code Block"),
...
transforms: {
from: [
{
type: "block",
priority: 7,
blocks: ["core/code", "core/paragraph", "core/preformatted"],
transform: function (attributes) {
return createBlock("my/code-block", {
content: attributes.content,
});
},
},
],
to: [
{
type: "block",
blocks: ["core/code"],
transform: ({ content }) => createBlock("core/code", { content }),
},
],
...
这些是到其他块和从其他块的转换。幸运的是,这是一个非常简单的块,我们只是在对content
进行排列。更复杂的块可能需要传递更多数据,但我还没有遇到过这种情况。
更神奇的东西:从原始代码进行块转换
这是对旧内容的真相时刻

在这种情况下,块不是从其他块创建的,而是从原始代码创建的。毫不夸张地说,HTML 正在被查看,并且正在做出关于从哪些 HTML 块中创建哪些块的选择。这就是块编辑器在做出选择时表现得如此出色,以及它可能出错并失败、做出错误的块选择或破坏内容的地方。
在我们的旧内容中,帖子中的代码块(一个非常重要的事情)看起来像这样
<pre rel="JavaScript"><code class="language-javascript" markup="tt">
let html = `<div>cool</div>`;
</code></pre>
有时块转换会对这些块进行良好处理,将其转换为原生代码块。但有一些问题
- 我不想使用原生代码块。我希望它转换为我们自己的新的代码块(此处对此进行了博客文章)。
- 我需要这些属性中的一些信息来告知新块的设置,例如代码类型。
- 我们旧代码块中的 HTML没有转义,我需要它不要被这些内容卡住。
我还没有所有答案,因为这是一个不断发展过程,但我现在已经有一些块转换正在运行,效果还不错。以下是“原始”转换(与“块”转换相对)的示例
registerBlockType("my/code-block", {
title: __("My Code Block"),
// ...
transforms: {
from: [
{
type: "block",
priority: 7,
// ...
},
{
type: "raw",
priority: 8,
isMatch: (node) =>
node.nodeName === "PRE" &&
node.children.length === 1 &&
node.firstChild.nodeName === "CODE",
transform: function (node) {
let pre = node;
let code = node.querySelector("code");
let codeType = "html";
if (pre.classList.contains("language-css")) {
codeType = "css";
}
if (pre.getAttribute("rel") === "CSS") {
codeType = "css";
}
if (pre.classList.contains("language-javascript")) {
codeType = "javascript";
}
if (code.classList.contains("language-javascript")) {
codeType = "javascript";
}
// ... other data wrangling...
return createBlock("csstricks/code-block", {
content: code.innerHTML,
codeType: codeType,
});
},
},
],
to: [
// ...
],
// ...
}
该isMatch
函数运行在找到的每个 HTML 节点上,因此这是在您需要的特殊情况下从该函数中返回true
的重大机会。请注意上面的代码中,我专门在寻找看起来像<pre ...><code ...>
的 HTML。当匹配时,转换将运行,并且我可以返回一个createBlock
调用,该调用会传递使用 JavaScript 从节点中提取的数据和内容。
另一个示例:粘贴 URL
“原始”转换不仅发生在您“转换为块”时。它们也发生在您将内容粘贴到块编辑器中时。您可能之前遇到过这种情况。假设您从某处复制了一些表格标记并将其粘贴到块编辑器中——它可能会作为表格粘贴。YouTube URL 可能会粘贴到嵌入中。这种类型的操作是为什么从 Word 文档等中复制/粘贴通常在块编辑器中运行良好。
假设您希望在将特定类型的 URL 粘贴到编辑器中时执行一些特殊行为。这是我使用我们自定义 CodePen 嵌入块时的状况。我想要它这样,如果你粘贴了一个 codepen.io URL,它会使用这个自定义块,而不是默认的嵌入。
这是一个“from”转换,它看起来像这样
{
type: "raw",
priority: 8, // higher number to beat out default
isMatch: (node) =>
node.nodeName === "P" &&
node.innerText.startsWith("https://codepen.io/"),
transform: function (node) {
return createBlock("cp/codepen-gutenberg-embed-block", {
penURL: node.innerText,
penID: getPenID(node.innerText), // helper function
});
},
}
所以……
它很乱吗?有点。但它强大到你所需要的一切。如果你有一个旧网站,其中有很多定制的 HTML、简码等等,那么进入块转换是唯一的选择。
我很高兴去参加了WPBlockTalk 并观看了 K. Adam White 关于简码的演讲,因为那里有一张幻灯片让我明白这甚至可能是可行的。有一些关于它的文档。
我想弄清楚的一件事是,是否可以在数据库中的所有旧内容上运行这些转换。这听起来有点可怕,但也像是某些情况下一个好主意。一旦我的转换真正稳定,我就可以看到这样做,这样当打开它时,所有旧内容都会在块编辑器中准备就绪。我只是不知道如何进行。
不过我很高兴能够在一定程度上掌控它,因为我现在超级喜欢块编辑器。用它写东西和构建内容很愉快。我喜欢Justin Tadlock 的说法
块系统不会消失。WordPress 已经超越了我们应该将块编辑器视为一个独立实体的程度。它是 WordPress 的一个组成部分,最终将触及编辑屏幕之外的更多领域。
它将继续存在。拥抱块编辑器并将其弯曲到我们的意志是关键。
- 我们到底叫它什么呢?“古腾堡”似乎已经不再合适了。感觉它会逐渐消失,尽管它的开发仍然发生在古腾堡插件中。我认为我只会称之为“块编辑器”,除非专门指代那个插件。
好文章。我很感兴趣,我可以使用 Transform 来完成我一直苦苦挣扎的事情。我使用一个门户网站来为用户创建私人的仪表板。因为这些仪表板是通过表单输入创建的,所以没有涉及编辑器。我可以编辑 CSS 和标题脚本,但这非常繁琐。所以,我想知道,我是否可以以某种方式使用 Transform 在 Gutengberg 中创建块,然后使用 Transform 将设计复制/复制到门户网站插件中?