WordPress Block-Transformationen

Avatar of Chris Coyier
Chris Coyier am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Dieses Jahr war für uns hier bei CSS-Tricks das Jahr von Gutenberg. Tatsächlich war das ein Ziel, das wir uns Ende letzten Jahres gesetzt haben. Wir sind viel weiter, als ich dachte, wir würden sein, wir verfassen alle *neuen* Inhalte im Block-Editor¹, und wir haben den Block-Editor für *alle* Inhalte aktiviert. Das bedeutet, wenn wir die meisten alten Beiträge öffnen, sehen wir den gesamten Inhalt im „Klassischen“ Block. Das sieht so aus:

Ein Beitrag, der auf CSS-Tricks geschrieben wurde, bevor wir den Block-Editor nutzten.

Der gesamte Inhalt des Beitrags befindet sich in einem einzigen Block, sodass der Block-Editor nicht wirklich genutzt wird. Er ist immer noch „visuell“, wie der Block-Editor, aber eher wie der alte visuelle Editor mit TinyMCE. Den habe ich nie benutzt, da er den HTML-Code auf eine Weise verunstaltete, die mir nicht gefiel.

Das ist das wichtigste, worüber ich mir Sorgen gemacht habe

Die Umwandlung eines klassischen Blocks in neue Blöcke ist so einfach wie die Auswahl des klassischen Blocks und die Auswahl der Option „In Blöcke konvertieren“.

Wählen Sie die Option, und der eine Block wird zu vielen Blöcken.

Wie geht der Block-Editor mit der „Block-Umwandlung“ alter Inhalte um, wenn wir ihn über die Option „In Blöcke konvertieren“ dazu auffordern? Was, wenn er die Inhalte während der Konvertierung total vermasselt? Können wir jemals zurückwechseln?

Die Antwort: *Er macht eine ziemlich gute Arbeit.* Aber... es gibt immer noch Probleme. Keine „Bugs“, sondern Situationen, in denen wir benutzerdefinierten HTML-Code in unseren alten Inhalten haben und er nicht weiß, was er damit machen soll – geschweige denn, wie er ihn genau in die Blöcke umwandeln soll, die wir uns wünschen. Es gibt einen Weg!

Grundlegende Block-Transformationen

Hier kommt die Idee der „Block-Transformationen“ ins Spiel. Alle (na ja, die meisten?) nativen Blöcke haben „Hin zu“- und „Von“-Transformationen. Wahrscheinlich sind Sie bereits vertraut damit, wie es sich in der Benutzeroberfläche manifestiert. Ein Absatz kann beispielsweise „in“ ein Zitat und umgekehrt transformiert werden. Hier ist ein super meta-Screenshot dieses Absatzes.

Diese Transformationen sind keine Magie; sie sind sehr explizit kodiert. Wenn Sie einen Block registrieren, legen Sie die Transformationen fest. Nehmen wir an, Sie würden Ihren eigenen benutzerdefinierten Codeblock registrieren. Sie würden sicherstellen wollen, dass Sie ihn transformieren können...

  • Von und zu dem standardmäßig integrierten Codeblock und wahrscheinlich einer Handvoll anderer, die nützlich sein könnten.
  • Zurück **zum** integrierten Codeblock.

Was vielleicht so aussieht:

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 }),
      },
    ],
   
   ...

Das sind Transformationen **zu und von anderen Blöcken**. Glücklicherweise ist dies ein ziemlich einfacher Block, bei dem wir nur den `content` verschieben. Komplexere Blöcke müssen möglicherweise mehr Daten übergeben, aber damit hatte ich bisher noch nicht zu tun.

Das Magischere: Block-Transformationen aus Rohcode

Hier ist der Moment der Wahrheit für alte Inhalte.

Die Option „In Blöcke konvertieren“.

In dieser Situation werden Blöcke nicht aus anderen Blöcken, sondern aus Rohcode erstellt. Ganz wörtlich wird das HTML betrachtet und Entscheidungen darüber getroffen, welche Blöcke aus Teilen dieses HTML erstellt werden sollen. Hier ist es erstaunlich, dass der Block-Editor bei den Entscheidungen so gute Arbeit leistet, und hier können die Dinge auch schiefgehen und er kann scheitern, falsche Block-Entscheidungen treffen oder Inhalte verfälschen.

In unseren alten Inhalten sah ein Codeblock (*eine super wichtige Sache*) in einem Beitrag so aus:

<pre rel="JavaScript"><code class="language-javascript" markup="tt">
  let html = `<div>cool</div>`;
</code></pre>

Manchmal funktionierte die Block-Konvertierung bei diesen gut und verwandelte sie in einen nativen Codeblock. Aber es gab eine Reihe von Problemen:

  1. Ich will keinen nativen Codeblock. Ich möchte, dass er in unseren eigenen *neuen* Codeblock umgewandelt wird (darüber haben wir hier gebloggt).
  2. Ich brauche einige Informationen aus diesen Attributen, um Einstellungen für den neuen Block zu beeinflussen, wie z. B. die Art des Codes.
  3. Das HTML in unseren alten Codeblöcken war *nicht escaped*, und ich brauche, dass es damit nicht abstürzt.

Ich habe hier nicht alle Antworten, da dies ein sich entwickelnder Prozess ist, aber ich habe jetzt einige Block-Transformationen, die ziemlich gut funktionieren. Hier ist, wie eine „Roh“-Transformation (im Gegensatz zu einer „Block“-Transformation) aussieht:

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: [
      // ... 
    ],
   
   // ...

}

Diese `isMatch`-Funktion läuft über jeden Knoten im gefundenen HTML, daher ist dies die große Gelegenheit, von diesem `true` in den speziellen Situationen zurückzukehren, die Sie benötigen. Beachten Sie im obigen Code, dass ich speziell nach HTML suche, das wie `

` aussieht. Wenn das übereinstimmt, wird die Transformation ausgeführt, und ich kann einen `createBlock`-Aufruf zurückgeben, der Daten und Inhalte übergibt, die ich mit JavaScript aus dem Knoten extrahiert habe.

Ein weiteres Beispiel: Einfügen einer URL

„Roh“-Transformationen erfolgen nicht nur, wenn Sie „In Blöcke konvertieren“. Sie geschehen auch, wenn Sie Inhalte in den Block-Editor einfügen. Das haben Sie wahrscheinlich schon erlebt. Sagen wir, Sie haben eine Tabellenmarkierung von irgendwoher kopiert und fügen sie in den Block-Editor ein – sie wird wahrscheinlich als Tabelle eingefügt. Eine YouTube-URL könnte in ein Embed eingefügt werden. Diese Art von Ding ist der Grund, warum das Kopieren und Einfügen von Word-Dokumenten usw. mit dem Block-Editor so gut funktioniert.

Sagen wir, Sie möchten ein besonderes Verhalten, wenn eine bestimmte Art von URL in den Editor eingefügt wird. Das war die Situation, in der ich mich mit unserem benutzerdefinierten CodePen Embed-Block befand. Ich wollte, dass beim Einfügen einer codepen.io-URL dieser benutzerdefinierte Block verwendet wird, anstelle des Standard-Embeds.

Das ist eine „Hin zu“-Transformation, die so aussieht:

{
  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
    });
  },
}

Also…

Ist es unübersichtlich? Ein wenig. Aber es ist so leistungsfähig, wie Sie es brauchen. Wenn Sie eine alte Website mit viel benutzerdefiniertem HTML und Shortcodes und so weiter haben, dann ist die Beschäftigung mit Block-Transformationen der einzige Ausweg.

Ich bin froh, dass ich zu WPBlockTalk gegangen bin und K. Adam Whites Vortrag über Shortcodes gehört habe, denn es gab nur eine Folie, die mich darauf aufmerksam machte, dass das überhaupt möglich ist. Es gibt einige Dokumentationen dazu.

Eine Sache, die ich herausfinden möchte, ist, ob es möglich ist, diese Transformationen auf *allen* alten Inhalten in der Datenbank auszuführen. Das scheint ein wenig beängstigend, aber auch so, als ob es in manchen Situationen eine gute Idee sein könnte. Sobald ich meine Transformationen wirklich solide habe, könnte ich mir vorstellen, das zu tun, damit alte Inhalte beim Öffnen im Block-Editor bereit sind. Ich habe nur keine Ahnung, wie ich vorgehen soll.

Ich bin froh, dass ich dem hier einigermaßen auf der Spur bin, denn ich liebe den Block-Editor im Moment. Es ist eine Freude, darin zu schreiben und Inhalte damit zu erstellen. Ich mag, was Justin Tadlock gesagt hat:

Das Blocksystem wird nicht verschwinden. WordPress ist über den Punkt hinaus, an dem wir den Block-Editor als separate Einheit betrachten sollten. Er ist ein integraler Bestandteil von WordPress und wird schließlich immer mehr Bereiche außerhalb des Bearbeitungsbildschirms berühren.

Es ist gekommen, um zu bleiben. Die Akzeptanz des Block-Editors und seine Anpassung an unsere Bedürfnisse ist der Schlüssel.

  1. Wie nennen wir es eigentlich? „Gutenberg“ scheint nicht mehr richtig zu sein. Es fühlt sich an, als würde das verblassen, auch wenn die Entwicklung davon immer noch im Gutenberg-Plugin stattfindet. Ich denke, ich werde es einfach „der Block-Editor“ nennen, es sei denn, ich beziehe mich speziell auf dieses Plugin.