Wenn Sie im Editor jemals die Meldung „Block wird als leer gerendert“ gesehen haben oder einen Block, der lokal funktioniert, aber in der Produktionsumgebung verschwindet, liegt das Problem fast immer an derselben Stelle: einer schlecht verbundenen Build-Konfiguration, schlecht gespeicherten Assets oder einem fehlenden Server-Rendering, wenn Ihr Block davon abhängt.

Das Problem / Der Bedarf

Sie wollen créer ein sauberer, wartungsfreundlicher und kompatibler benutzerdefinierter Gutenberg-Block WordPress 6.9.4 (April 2026), ohne für jede Iteration manuell eine Skriptabfrage erstellen zu müssen. Die typische Anforderung: ein „Alert Box“-Block (oder „CTA“, „FAQ“, „Produkt“ usw.) mit:

  • un React-Editor (Inspektor-Steuerelemente, Farben, Optionen)
  • un zuverlässig gemacht (ohne Abhängigkeit von einer JS-Laufzeitumgebung auf Besucherseite),
  • vereint moderne Baukette @wordpress/scripts (Webpack/Babel sofort einsatzbereit),
  • vereint Anlagenregistrierung robust über block.json und die Metadaten.

Am Ende werden Sie wissen, wie man einen kompletten Block in folgender Form liefert: Pluginmit Build-Prozessen, Versionsverwaltung, Internationalisierung und serverseitigem Rendering (dynamischer Block), sofern relevant. Ich betone dynamische Blöcke, da ich oft erlebt habe, wie „statische“ Blöcke unübersichtlich werden, sobald der Inhalt von globalen Optionen, einem benutzerdefinierten Beitragstyp (CPT) oder einem Kontext abhängt.

Kurze Zusammenfassung

  • Wir erstellen ein Plugin, das einen Block speichert über block.json et register_block_type().
  • Wir verwenden @wordpress/scripts zum Bauen src/index.js zu build/index.js + Datei .asset.php.
  • Wir schreiben einen Block mit Benutzeroberfläche auf der Editorseite (JS) und serverseitiges Rendering (PHP), um Diskrepanzen zwischen Frontend und Editor zu vermeiden.
  • Wir laden CSS/JS über die Felder editorScript, style, editorStyle du block.json.
  • Wir gehen auf die Punkte ein, die im Produktivbetrieb zu Fehlern führen: Pfade, Cache, Abhängigkeiten, PHP-Version und Registrierungs-Hooks.

Wann sollte diese Lösung verwendet werden?

  • Du willst einen Block wiederverwendbar von plusieurs Websites, die als Plugin bereitgestellt werden.
  • Du brauchst ein stabilisiert Frontend-Seite (SEO, Performance, Cache-Kompatibilität): Dynamische Blockierung empfohlen.
  • Sie möchten das Kopieren und Einfügen eines Abfrage-Snippets vermeiden und stattdessen den Standardablauf verwenden. WordPress : block.json + bauen.
  • Ihr Block sollte sich weiterentwickeln (neue Optionen, Variationen, Stile), ohne den bestehenden Inhalt zu beeinträchtigen.
  • Ihr arbeitet als Team: der Aufbau via @wordpress/scripts standardisiert die Umgebung.

Wann diese Lösung NICHT verwendet werden sollte

  • Sie benötigen lediglich einfachen Inhalt: a Blockmuster Das Blockmuster ist oft ausreichend, auch ohne JavaScript. Siehe Blockmuster.
  • Sie suchen nach einem komplexen, aber unlogischen Layout: a Gruppenblock + Globale Stile + Variationen können einen benutzerdefinierten Block ersetzen.
  • Sie können einen Build-Schritt (CI/CD, Node) nicht verwalten: Verwenden Sie in diesem Fall ein Builder-Plugin (Scaffold). Commit des Build-Ordners und vermeiden Sie den Betrieb von Node in der Produktion.
  • Sie befinden sich in einer abgeschotteten Umgebung, in der Node verboten ist: Sie können zwar die erstellten Dateien ausliefern, verlieren aber den Entwicklungszyklus.

Voraussetzungen / vor Beginn

Ich gehe davon aus, dass Sie für WordPress 6.9.4 und PHP 8.1+ entwickeln. Viele ältere Anleitungen funktionieren heute nicht mehr, weil sie diese Technologien nicht nutzen. block.json richtig oder vergessen Sie das .asset.php.

  • WordPress : 6.9.4 (oder neuer).
  • PHP 8.1+ (empfohlen). Referenz: Unterstützte PHP-Versionen.
  • Node.js Eine aktuelle LTS-Version (18/20/22, abhängig von Ihrem Stack) ist empfehlenswert. Vermeiden Sie EOL-Versionen.
  • Zugang : eine Test-/lokale Umgebung. Testen Sie einen Block-Build nicht direkt in der Produktionsumgebung ohne vorheriges Backup.
  • Werkzeuge :
    • WP-CLI (optional, aber nützlich).
    • Ein Protokoll-Plugin (oder Zugriff) debug.log).

Vorsichtsmaßnahmen:

  • ermöglichen WP_DEBUG et WP_DEBUG_LOG in Ihrer Testumgebung.
  • Wenn Sie einen Cache verwenden (Plugin, Varnish, Cloudflare), sollten Sie diesen regelmäßig leeren: Ich habe oft erlebt, dass blockiertes CSS nicht geladen wurde, obwohl es nur an einem aggressiven Cache lag.

Nützliche offizielle Dokumente:

Der naive Ansatz (und warum man ihn vermeiden sollte)

Der Klassiker, den ich auch 2026 noch sehen werde: ein „handgefertigter“ Block mit einem großen wp_enqueue_script in wp_enqueue_scriptsOhne .asset.php, ohne deklarierte Abhängigkeiten, und manchmal sogar ohne block.jsonEs funktioniert bei mir, dann aber nach einem WordPress/Gutenberg-Update nicht mehr.

Ein naives Beispiel (nicht nachzuahmen)

<?php
// Mauvaise pratique : charge partout, pas seulement dans l'éditeur, dépendances non gérées.
add_action( 'wp_enqueue_scripts', function () {
	wp_enqueue_script(
		'mon-bloc',
		plugins_url( 'build/index.js', __FILE__ ),
		array( 'wp-blocks', 'wp-element', 'wp-editor' ), // Souvent faux/incomplet.
		'1.0.0',
		true
	);
} );

Warum dies ein Problem darstellt:

  • Leistung : Der JS-Code des Blocks wird an das gesamte Frontend ausgeliefert, selbst wenn kein Block verwendet wird.
  • Nebengebäude Die manuell erstellte Liste ist am Ende immer falsch. WordPress generiert eine .asset.php Genau aus diesem Grund.
  • Wartung Sie duplizieren die Logik der Anlagenregistrierung, anstatt Metadaten zu verwenden.
  • Konfliktgefahr : globale Handles, Kollisionen und Skripte, die im falschen Kontext geladen wurden.

Die richtige Herangehensweise – eine Schritt-für-Schritt-Anleitung

Ziel: ein Plugin bpca-alert-block wodurch ein „Warnfeld“-Block mit folgendem Inhalt hinzugefügt wird:

  • ein Titel + Inhalt,
  • eine Ebene (Info/Erfolg/Aufmerksamkeit/Fehler),
  • Option „Symbol“ (ja/nein),
  • Front-End-Rendering in PHP (dynamischer Block), um Konsistenz zu gewährleisten und zukünftige Entwicklungen zu ermöglichen.

Schritt 1 – Plugin-Struktur erstellen

Tanz wp-content/plugins/ :

mkdir -p bpca-alert-block/src bpca-alert-block/build bpca-alert-block/assets

Erstellen Sie die Hauptdatei:

<?php
/**
 * Plugin Name: BPCA Alert Block
 * Description: Bloc Gutenberg "Encart Alerte" (dynamic block) avec build via @wordpress/scripts.
 * Version: 1.0.0
 * Requires at least: 6.9
 * Requires PHP: 8.1
 * Author: BPCA
 * License: GPL-2.0-or-later
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

define( 'BPCA_ALERT_BLOCK_FILE', __FILE__ );
define( 'BPCA_ALERT_BLOCK_DIR', __DIR__ );

require_once BPCA_ALERT_BLOCK_DIR . '/includes/class-bpca-alert-block.php';

add_action( 'init', array( 'BPCA\AlertBlock\Plugin', 'init' ) );

schaffen includes/class-bpca-alert-block.php :

<?php
namespace BPCAAlertBlock;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

final class Plugin {

	public static function init(): void {
		// Enregistre le bloc via block.json + render_callback.
		add_action( 'init', array( __CLASS__, 'register_block' ) );
	}

	public static function register_block(): void {
		$block_json = BPCA_ALERT_BLOCK_DIR . '/block.json';

		// register_block_type() sait lire block.json et enregistrer les assets déclarés.
		register_block_type(
			$block_json,
			array(
				'render_callback' => array( __CLASS__, 'render' ),
			)
		);
	}

	/**
	 * Rendu serveur (dynamic block).
	 *
	 * @param array  $attributes Attributs du bloc.
	 * @param string $content    Contenu interne (InnerBlocks), non utilisé ici.
	 * @return string HTML rendu.
	 */
	public static function render( array $attributes, string $content ): string {
		$level   = isset( $attributes['level'] ) ? sanitize_key( $attributes['level'] ) : 'info';
		$title   = isset( $attributes['title'] ) ? sanitize_text_field( $attributes['title'] ) : '';
		$message = isset( $attributes['message'] ) ? wp_kses_post( $attributes['message'] ) : '';
		$icon    = ! empty( $attributes['showIcon'] );

		$allowed_levels = array( 'info', 'success', 'warning', 'error' );
		if ( ! in_array( $level, $allowed_levels, true ) ) {
			$level = 'info';
		}

		$classes = array(
			'bpca-alert',
			'bpca-alert--' . $level,
		);

		$icon_html = '';
		if ( $icon ) {
			// Icônes simples en SVG inline (pas de dépendance externe).
			$icon_html = '<span class="bpca-alert__icon" aria-hidden="true">' . self::get_icon_svg( $level ) . '</span>';
		}

		$title_html = '';
		if ( $title !== '' ) {
			$title_html = '<div class="bpca-alert__title">' . esc_html( $title ) . '</div>';
		}

		// Note : $message est déjà filtré via wp_kses_post() mais on l'échappe en contexte HTML.
		$message_html = '';
		if ( $message !== '' ) {
			$message_html = '<div class="bpca-alert__message">' . $message . '</div>';
		}

		$html  = '<div class="' . esc_attr( implode( ' ', $classes ) ) . '" role="note">';
		$html .= $icon_html;
		$html .= '<div class="bpca-alert__body">' . $title_html . $message_html . '</div>';
		$html .= '</div>';

		return $html;
	}

	private static function get_icon_svg( string $level ): string {
		// SVG minimalistes. Vous pouvez les remplacer par vos propres assets.
		switch ( $level ) {
			case 'success':
				return '<svg viewBox="0 0 24 24" width="20" height="20" focusable="false"><path d="M9 16.2 4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4z"></path></svg>';
			case 'warning':
				return '<svg viewBox="0 0 24 24" width="20" height="20" focusable="false"><path d="M1 21h22L12 2 1 21zm12-3h-2v2h2v-2zm0-8h-2v6h2V10z"></path></svg>';
			case 'error':
				return '<svg viewBox="0 0 24 24" width="20" height="20" focusable="false"><path d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"></path></svg>';
			case 'info':
			default:
				return '<svg viewBox="0 0 24 24" width="20" height="20" focusable="false"><path d="M11 17h2v-6h-2v6zm0-8h2V7h-2v2zm1-7C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2z"></path></svg>';
		}
	}
}

Schritt 2 — Block.json hinzufügen (Metadaten + Assets)

schaffen block.json im Stammverzeichnis des Plugins:

{
	"$schema": "https://schemas.wp.org/trunk/block.json",
	"apiVersion": 3,
	"name": "bpca/alert",
	"title": "Encart Alerte (BPCA)",
	"category": "widgets",
	"icon": "warning",
	"description": "Affiche un encart d'alerte avec niveau et option d'icône.",
	"textdomain": "bpca-alert-block",
	"attributes": {
		"level": { "type": "string", "default": "info" },
		"title": { "type": "string", "default": "" },
		"message": { "type": "string", "default": "" },
		"showIcon": { "type": "boolean", "default": true }
	},
	"supports": {
		"anchor": true,
		"html": false
	},
	"editorScript": "file:./build/index.js",
	"style": "file:./build/style-index.css",
	"editorStyle": "file:./build/index.css"
}

Praktische Hinweise:

  • apiVersion: 3 ist die aktuelle Grundlage für moderne Wohnblöcke.
  • Das Feld file: löst das automatische Speichern von Assets mit Versionen über die .asset.php erzeugt.
  • supports.html: false verhindert, dass der Benutzer "in HTML bearbeitet" und die Struktur beschädigt.

Schritt 3 – Installieren Sie @wordpress/scripts und konfigurieren Sie package.json

Im Plugin-Ordner:

npm init -y
npm install --save-dev @wordpress/scripts

ersetzen package.json (oder anpassen):

{
	"name": "bpca-alert-block",
	"version": "1.0.0",
	"private": true,
	"scripts": {
		"start": "wp-scripts start",
		"build": "wp-scripts build",
		"lint:js": "wp-scripts lint-js",
		"format": "wp-scripts format"
	},
	"devDependencies": {
		"@wordpress/scripts": "^30.0.0"
	}
}

Reference: @wordpress/scripts.

Schritt 4 — Schreiben Sie den Code für den Block (Editor) in src/

schaffen src/index.js :

import { registerBlockType } from '@wordpress/blocks';
import { __ } from '@wordpress/i18n';
import {
	InspectorControls,
	useBlockProps,
	RichText,
} from '@wordpress/block-editor';
import {
	PanelBody,
	SelectControl,
	ToggleControl,
	TextControl,
} from '@wordpress/components';

import './editor.css';
import './style.css';

registerBlockType( 'bpca/alert', {
	edit: ( { attributes, setAttributes } ) => {
		const { level, title, message, showIcon } = attributes;

		const blockProps = useBlockProps( {
			className: `bpca-alert bpca-alert--${ level }`,
		} );

		return (
			<>
				<InspectorControls>
					<PanelBody title={ __( 'Réglages', 'bpca-alert-block' ) }>
						<SelectControl
							label={ __( 'Niveau', 'bpca-alert-block' ) }
							value={ level }
							options={ [
								{ label: __( 'Info', 'bpca-alert-block' ), value: 'info' },
								{ label: __( 'Succès', 'bpca-alert-block' ), value: 'success' },
								{ label: __( 'Attention', 'bpca-alert-block' ), value: 'warning' },
								{ label: __( 'Erreur', 'bpca-alert-block' ), value: 'error' },
							] }
							onChange={ ( next ) => setAttributes( { level: next } ) }
						/>

						<ToggleControl
							label={ __( 'Afficher une icône', 'bpca-alert-block' ) }
							checked={ !! showIcon }
							onChange={ ( next ) => setAttributes( { showIcon: !! next } ) }
						/>

						<TextControl
							label={ __( 'Titre (optionnel)', 'bpca-alert-block' ) }
							value={ title }
							onChange={ ( next ) => setAttributes( { title: next } ) }
						/>
					</PanelBody>
				</InspectorControls>

				<div { ...blockProps }>
					{ showIcon && (
						<span className="bpca-alert__icon" aria-hidden="true">
							<span className="bpca-alert__icon-placeholder">!</span>
						</span>
					) }

					<div className="bpca-alert__body">
						{ title ? (
							<div className="bpca-alert__title">{ title }</div>
						) : null }

						<RichText
							tagName="div"
							className="bpca-alert__message"
							value={ message }
							allowedFormats={ [ 'core/bold', 'core/italic', 'core/link' ] }
							placeholder={ __( 'Votre message…', 'bpca-alert-block' ) }
							onChange={ ( next ) => setAttributes( { message: next } ) }
						/>
					</div>
				</div>
			</>
		);
	},

	// Dynamic block : save() doit retourner null.
	save: () => null,
} );

Zwei CSS-Dateien:

/* src/style.css - CSS front + éditeur (partagé) */
.bpca-alert{
	display:flex;
	gap:12px;
	padding:14px 16px;
	border-radius:10px;
	border:1px solid transparent;
	align-items:flex-start;
}
.bpca-alert__icon svg{ display:block; fill: currentColor; }
.bpca-alert__title{ font-weight: 650; margin-bottom: 6px; }
.bpca-alert__message a{ text-decoration: underline; }

.bpca-alert--info{ background:#eef6ff; border-color:#cfe6ff; color:#0b3d91; }
.bpca-alert--success{ background:#eafff1; border-color:#c9f2d7; color:#0b5d2a; }
.bpca-alert--warning{ background:#fff7e6; border-color:#ffe3a3; color:#7a4a00; }
.bpca-alert--error{ background:#ffecec; border-color:#ffc2c2; color:#7a0000; }
/* src/editor.css - uniquement éditeur */
.wp-block-bpca-alert .bpca-alert__icon-placeholder{
	display:inline-flex;
	width:20px;
	height:20px;
	border-radius:4px;
	align-items:center;
	justify-content:center;
	background: rgba(0,0,0,.08);
	font-weight: 700;
}

Schritt 5 – Bauarbeiter

Start:

npm run build

Sie müssen Folgendes beschaffen:

  • build/index.js
  • build/index.asset.php (kritisch)
  • build/index.css
  • build/style-index.css

Schritt 6 – Aktivieren Sie das Plugin und testen Sie es.

  • Aktivieren Sie das Plugin im Admin-Bereich.
  • Im Blockeditor nach „Alert Box (BPCA)“ suchen.
  • Füge es hinzu, ändere die Ebene, teste mit/ohne Symbol, veröffentliche.

Vollständiger Code

Nachfolgend finden Sie eine funktionierende Kopie (vollständiges Plugin). Beachten Sie, dass der Build (build/) ist hier nicht enthalten: Sie müssen ausführen npm run build um die Dateien zu generieren.

1) bpca-alert-block.php

<?php
/**
 * Plugin Name: BPCA Alert Block
 * Description: Bloc Gutenberg "Encart Alerte" (dynamic block) avec build via @wordpress/scripts.
 * Version: 1.0.0
 * Requires at least: 6.9
 * Requires PHP: 8.1
 * Author: BPCA
 * License: GPL-2.0-or-later
 * Text Domain: bpca-alert-block
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

define( 'BPCA_ALERT_BLOCK_FILE', __FILE__ );
define( 'BPCA_ALERT_BLOCK_DIR', __DIR__ );

require_once BPCA_ALERT_BLOCK_DIR . '/includes/class-bpca-alert-block.php';

add_action( 'init', array( 'BPCA\AlertBlock\Plugin', 'init' ) );

2) includes/class-bpca-alert-block.php

<?php
namespace BPCAAlertBlock;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

final class Plugin {

	public static function init(): void {
		add_action( 'init', array( __CLASS__, 'register_block' ) );
	}

	public static function register_block(): void {
		register_block_type(
			BPCA_ALERT_BLOCK_DIR . '/block.json',
			array(
				'render_callback' => array( __CLASS__, 'render' ),
			)
		);
	}

	public static function render( array $attributes, string $content ): string {
		$level   = isset( $attributes['level'] ) ? sanitize_key( $attributes['level'] ) : 'info';
		$title   = isset( $attributes['title'] ) ? sanitize_text_field( $attributes['title'] ) : '';
		$message = isset( $attributes['message'] ) ? wp_kses_post( $attributes['message'] ) : '';
		$icon    = ! empty( $attributes['showIcon'] );

		$allowed_levels = array( 'info', 'success', 'warning', 'error' );
		if ( ! in_array( $level, $allowed_levels, true ) ) {
			$level = 'info';
		}

		$classes = array( 'bpca-alert', 'bpca-alert--' . $level );

		$icon_html = '';
		if ( $icon ) {
			$icon_html = '<span class="bpca-alert__icon" aria-hidden="true">' . self::get_icon_svg( $level ) . '</span>';
		}

		$title_html = $title !== '' ? '<div class="bpca-alert__title">' . esc_html( $title ) . '</div>' : '';
		$message_html = $message !== '' ? '<div class="bpca-alert__message">' . $message . '</div>' : '';

		return '<div class="' . esc_attr( implode( ' ', $classes ) ) . '" role="note">'
			. $icon_html
			. '<div class="bpca-alert__body">' . $title_html . $message_html . '</div>'
			. '</div>';
	}

	private static function get_icon_svg( string $level ): string {
		switch ( $level ) {
			case 'success':
				return '<svg viewBox="0 0 24 24" width="20" height="20" focusable="false"><path d="M9 16.2 4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4z"></path></svg>';
			case 'warning':
				return '<svg viewBox="0 0 24 24" width="20" height="20" focusable="false"><path d="M1 21h22L12 2 1 21zm12-3h-2v2h2v-2zm0-8h-2v6h2V10z"></path></svg>';
			case 'error':
				return '<svg viewBox="0 0 24 24" width="20" height="20" focusable="false"><path d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"></path></svg>';
			case 'info':
			default:
				return '<svg viewBox="0 0 24 24" width="20" height="20" focusable="false"><path d="M11 17h2v-6h-2v6zm0-8h2V7h-2v2zm1-7C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2z"></path></svg>';
		}
	}
}

3) block.json

{
	"$schema": "https://schemas.wp.org/trunk/block.json",
	"apiVersion": 3,
	"name": "bpca/alert",
	"title": "Encart Alerte (BPCA)",
	"category": "widgets",
	"icon": "warning",
	"description": "Affiche un encart d'alerte avec niveau et option d'icône.",
	"textdomain": "bpca-alert-block",
	"attributes": {
		"level": { "type": "string", "default": "info" },
		"title": { "type": "string", "default": "" },
		"message": { "type": "string", "default": "" },
		"showIcon": { "type": "boolean", "default": true }
	},
	"supports": {
		"anchor": true,
		"html": false
	},
	"editorScript": "file:./build/index.js",
	"style": "file:./build/style-index.css",
	"editorStyle": "file:./build/index.css"
}

4) src/index.js + src/style.css + src/editor.css

Gehen Sie zurück und sehen Sie sich die Dateien aus dem Abschnitt „Schritt für Schritt“ noch einmal an.

Code-Erklärung

Warum block.json das Leben wirklich vereinfacht

block.json ist zum zentralen Punkt geworden. WordPress liest die Metadaten, speichert den Block und weiß, wie die Ressourcen an der richtigen Stelle geladen werden (Editor vs. Frontend). Wenn Sie verwenden "file:./build/index.js"WordPress ist auf die Datei angewiesen. build/index.asset.php generiert durch den Build für:

  • erklären genaue Abhängigkeiten (Ex: wp-element, wp-i18n, wp-block-editor),
  • erklären Version (Hash) für Cache-Busting.

Offizielle Referenz: Blockmetadaten.

Warum ein dynamischer Block (save: null) echte Fehler vermeidet

Bei einem statischen Block müssen Sie Folgendes beibehalten: zwei Darstellungen :

  • das JSX-Rendering in edit(),
  • serialisiertes HTML save().

In der Praxis vergisst man, eine hinzugefügte Option zu aktualisieren. save()Und so erhält man Inhalte im „alten Format“ in der Datenbank. Bei dynamischer Blockarchitektur verwendet das Frontend PHP, daher:

  • Sie können den HTML-Code weiterentwickeln, ohne Inhalte zu migrieren.
  • Sie zentralisieren die Abgas- und Abwassersysteme.
  • Sie verwalten Kontexte besser (mehrsprachige Website, globale Optionen, A/B-Tests usw.).

Der Nachteil: Die Darstellung ist serverseitig, daher muss man streng auf Leistung und Caching achten (darauf kommen wir später zurück).

Haken und Timing

Wir speichern den Block auf initDies ist der erwartete Zeitablauf: Blocktypen müssen früh genug registriert werden, jedoch erst nachdem der Kern geladen wurde. Referenz: register_block_type().

Desinfektion und Flucht

  • sanitize_key() befolgen für level (Wert „Slug“).
  • sanitize_text_field() befolgen für title.
  • wp_kses_post() befolgen für message weil eine Teilmenge von HTML (Links, Hervorhebungen) zulässig ist. Dokument: wp_kses_post().
  • esc_attr() bezüglich der CSS-Klasse esc_html() zum Titel.

Bei dieser Art von Block besteht das Hauptrisiko in HTML-Injection, wenn man den Attributen vertraut. Selbst wenn der Bearbeiter Administratorrechte besitzt, ist eine dauerhafte XSS-Schwachstelle unerwünscht, wenn auch Benutzer mit niedrigeren Berechtigungen veröffentlichen können.

Varianten und Anwendungsfälle

Option 1 — Statischer Block (falls Sie auf serialisiertem HTML bestehen)

Anwendungsfall: Sie möchten, dass die Inhalte vollständig portabel sind, ohne vom Plugin abhängig zu sein (z. B. Export auf eine andere Website ohne das Plugin). Sie können Folgendes implementieren: save() und entfernen render_callback.

Was ich in diesem Fall mache: Ich halte den HTML-Code minimal in save() Und ich beschränke die Optionen. Andernfalls werden Sie sich früher als erwartet mit Blockversionsmigrationen auseinandersetzen müssen.

Option 2 – Blockstile (Stilvarianten) hinzufügen, ohne die Benutzeroberfläche zu verkomplizieren

Sie können Stile über JS deklarieren (registerBlockStyle) oder über block.json Das hängt von Ihrer Strategie ab. Für fortgeschrittene Blogger ist es oft einfacher, dem Nutzer die Wahl zwischen „Gliederung“ und „Ausgefüllt“ zu lassen, ohne im Inspektor einen Schalter hinzuzufügen.

Dokument: Blockstile.

Variante 3 – Rendering abhängig von einer globalen Option (Settings API)

Beispiel: Sie möchten eine Option „Markenfarben“ einmalig konfigurieren. render()Wählen Sie eine Option aus und passen Sie die Klassen oder den Inline-Stil an (halten Sie es einfach). Achtung: Wenn Sie Inline-CSS blockweise generieren, kann dies die HTML-Größe auf langen Seiten drastisch erhöhen.

Divi 5 / Elementor / Avada-Kompatibilität

Wichtig: Divi 5, Elementor und Avada können parallel zum Blockeditor verwendet werden, Inhalte lassen sich jedoch mit den jeweiligen Editoren erstellen. Ihr Gutenberg-Block bleibt weiterhin nutzbar.

  • im nativen Blockeditor,
  • in den Bereichen „Gutenberg“/„Block-Editor“, die diese Themes/Plugins bereitstellen,
  • manchmal in separaten Modulen (abhängig vom Entwickler).

Divi 5

Divi 5 bietet eine bessere Interoperabilität mit Blöcken, aber ich habe immer noch Probleme damit festgestellt, dass globales CSS von Divi Blockstile (Zeilenhöhe, Boxgröße) überschreibt. Ihr Block ist robust, wenn:

  • Sie kennzeichnen Ihre Klassen mit einem Präfix (bpca-alert),
  • Sie vermeiden allzu generische Selektoren.

Wenn Sie es als „Divi-Modul“ integrieren möchten, können Sie einen Shortcode (Fallback) bereitstellen, der dieselbe Rendering-Funktion wiederverwendet, aber duplizieren Sie die Logik nicht. Verwenden Sie nur eine einzige Datenquelle (PHP).

Elementor

Elementor ermöglicht das Einfügen von Shortcodes und, je nach Konfiguration, Blöcken über WordPress-Widgets. Für fortgeschrittene Anwender:

  • Schlagen Sie einen Kurzcode vor [bpca_alert] was die gleiche Rendering-Methode erfordert,
  • oder ein dediziertes Elementor-Widget einbinden (längerer Wartungsaufwand).

Ich empfehle den Shortcode als unkompliziertes Gateway, wenn Ihre Zielgruppe heterogen ist.

Avada (Fusion Builder)

Avada hatte in der Vergangenheit viel globales CSS. Insbesondere test:

  • die Standardränder auf div,
  • Linkstile,
  • die geerbten Farben.

Im Konfliktfall fügen Sie eine spezifischere CSS-Ebene hinzu in src/style.css (ohne in den Krieg hineingezogen zu werden) !important).

Prüfungen nach der Installation

  • Herausgeber Der Block wird angezeigt, und die Steuerelemente ändern die Vorschau korrekt.
  • Vorderreifen Die HTML-Darstellung enthält bpca-alert et bpca-alert--{level}.
  • Details :
    • im Herausgeber: build/index.js + build/index.css geladen,
    • auf der Vorderseite: build/style-index.css Wird nur geladen, wenn der Block vorhanden ist.
  • Cache : gemäß npm run build, Cache-Plugin/CDN leeren + Browser neu laden.
  • Logs : Es gibt keine Fehlermeldung „Ressource konnte nicht geladen werden“ im CSS/JS des Blocks.

Schnelldiagnosetabelle

Symptom Mögliche Ursache Überprüfung Lösung
Der Block wird im Einfügen nicht angezeigt. Plugin inaktiv oder PHP-Fehler beim Laden Admin > Erweiterungen, + debug.log Beheben Sie den Fehler, überprüfen Sie den Namensraum/die eingebundene Datei.
Sichtbarer, aber „leerer“ Block an der Vorderseite Dynamischer Block ohne render_callback effektiv oder Fehler in render() HTML-Quelltext und PHP-Protokolle anzeigen überprüfen register_block_type(... render_callback ...) und Hygiene
Im Frontend fehlt CSS. style falsch deklariert in block.json oder fehlender Build Registerkarte „Netzwerk“, Datei style-index.css beleben npm run build, Pfade überprüfen file:
JavaScript-Fehler im Editor Nicht aufgelöste Abhängigkeiten / fehlerhafter Build Browserkonsole im Editor Entfernen build/Build neu starten, prüfen index.asset.php
Änderungen nach dem Build nicht sichtbar Browser-Cache/CDN Asset-Hashes vergleichen Cache leeren + Neustart erzwingen

Wenn es nicht funktioniert

1) Überprüfen Sie zunächst den Build.

  • build/index.asset.php Existiert es? Falls es fehlt, kann WordPress die Abhängigkeiten nicht verwalten.
  • Du hast das gut gemacht npm run build im Plugin-Ordner (nicht im Projektstammverzeichnis)?

2) Überprüfen Sie die Pfade

Das Feld "file:./build/index.js" steht im Zusammenhang mit block.jsonWenn Sie umgezogen sind block.json In einem Unterordner funktioniert nichts mehr. Das ist ein häufiger Fehler beim Reorganisieren eines Repositorys.

3) Überprüfen Sie den Haken

Anmeldung am initWenn Sie zu früh speichern (z. B. beim Laden der Datei ohne Hook), kann es passieren, dass Funktionen gemäß der Ladereihenfolge nicht bereit sind.

4) Überprüfen Sie die PHP-Version

Ich sehe immer noch Hosting-Konten, bei denen die Website mit PHP 7.4/8.0 läuft, obwohl das Plugin für 8.1 geschrieben wurde. Die Folge: schwerwiegende Fehler. Überprüfen Sie dies unter „Tools > Website-Integrität“ oder über … phpinfo().

5) Plugin-/Snippet-Konflikte

Ein „Snippet-Plugin“ kann eine Datei beschädigen, wenn Sie Code ohne Klammern einfügen. Falls Ihre Website nach der Aktivierung abstürzt:

  • Deaktivieren Sie das Plugin per FTP (benennen Sie den Ordner um).
  • den Fehler in debug.log,
  • Reaktivieren.

Häufige Fallstricke und Fehler

Fehler Verursachen Lösung
Kopieren Sie den PHP-Code nach functions.php anstelle eines Plugins Das Thema ändert sich, der Block verschwindet. Verpacke es als Plugin, versioniere es und stelle es sauber bereit.
Parse error: syntax error, unexpected ... Fehlendes Semikolon/fehlende Klammer Lies den Diff erneut, aktiviere einen PHP-Linter, überprüfe debug.log
Der Block wird angezeigt, aber die Steuerelemente reagieren nicht. Falsch deklarierte Attribute oder Tippfehler in setAttributes überprüfen block.json vs attributes wird in JS verwendet
Failed to load resource von index.js Build fehlt, falscher Pfad oder Datei wurde nicht bereitgestellt einsetzen build/ Im Produktionsbetrieb die Berechtigungen prüfen.
CSS des nicht geladenen Blocks Verstecken oder style/editorStyle invertiert Cache leeren, überprüfen block.jsonTesten Sie im privaten Browsermodus
Verwechslung von Aktien und Filtern Die Verwendung von add_filter statt add_action von init Verwendung add_action( 'init', ... )
Block nach Update „defekt“ fest codierte Abhängigkeiten anstelle von .asset.php Vorbeigehen file: + Standardausführung
Direkter Test in der Produktionsumgebung ohne Backup Risiko eines schwerwiegenden Fehlers und Ausfallzeiten Kontrolliertes Staging, Backup und Deployment (ZIP/Release)
Permalinks „werden neu generiert“ (Nebenwirkung) Das kommt selten vor, aber manche Plugins modifizieren die Rewrite-Regeln bei der Aktivierung. Speichern Sie die Permalinks erneut, falls nach der Aktivierung ungewöhnliches Verhalten auftritt.

Sicherheits-, Leistungs- und Wartungstipps

Sicherheit

  • Behandeln alle Die Attribute sind als unzuverlässig gekennzeichnet. Selbst wenn sich die Benutzeroberfläche im Admin-Bereich befindet, können Inhalte per REST oder Import eingebunden werden.
  • Wenn Sie Ihrem Block REST-Endpunkte hinzufügen, verwenden Sie permissions_callback, Nuntien und entsprechende Kapazitäten.
  • Vermeiden Sie das Rendern von rohem HTML aus Attributen ohne wp_kses_*.

Leistung

  • Dynamischer Block: beibehalten render() schnell, ohne aufwändige Blockabfragen.
  • Wenn Sie Abfragen durchführen müssen (z. B. CPT), verwenden Sie den Objektcache und vermeiden Sie N+1.
  • Halten Sie Ihr CSS so minimalistisch wie möglich. Die Blöcke wiederholen sich: 20 Warnmeldungen auf einer Seite = Ihr CSS muss konsistent bleiben.

Wartung (tatsächliche Bereitstellung)

  • Nicht für die Produktionsumgebung erstellen. In der CI-Umgebung erstellen und das Plugin mit dieser Umgebung ausliefern. build/ versioniert (oder einer Release-Version zugeordnet).
  • Um zu vermeiden, dass je nach Maschine unterschiedliche Builds erstellt werden, sollte die Node-Version in der Entwicklungsumgebung (nvm, volta) festgelegt werden.
  • Entwicklungen in Gutenberg beobachten: Das beste Signal sind nach wie vor die PRs auf github.com/WordPress/gutenberg.

Ressourcen

FAQ

Warum sollte ich @wordpress/scripts anstelle meines eigenen Webpack verwenden?

Weil Sie den Wartungsaufwand reduzieren. @wordpress/scripts hält sich an die WordPress-Konventionen (Babel, WP-Abhängigkeiten, Generierung) .asset.phpEin individuell zusammengestelltes Webpack-Paket divergiert oft und bricht beim nächsten wichtigen Schritt ab.

Muss ich den Ordner build/ in Git einchecken?

Für ein auf einer Website bereitgestelltes Plugin gilt: Ja, Sie müssen liefern build/Ob Sie es committen oder an eine CI-Release anhängen, ist egal, aber die Produktion sollte nicht von einem Node-Build abhängen.

Warum lädt mein Block seine Stile nicht im Frontend?

In 80 % der Fälle: style falsch deklariert in block.json, nicht bereitgestellter Build oder Cache. Überprüfen Sie das. build/style-index.css Existiert und ist zugänglich.

Dynamische Blöcke: Sind sie schlecht für den Cache?

Nicht unbedingt. Der HTML-Code wird beim Generieren der Seite gerendert und anschließend wie alles andere zwischengespeichert. Das eigentliche Problem entsteht, wenn Ihre render() führt ressourcenintensive Anfragen aus, die nicht im Cache gespeichert werden, oder ist vom Benutzerstatus abhängig (in diesem Fall wird der Cache fragmentiert).

Kann ich InnerBlocks mit diesem Ansatz verwenden?

Ja. Sie müssen: (1) erklären supports.inserter Je nach Ihren Bedürfnissen (2) verwalten $content in render() und es korrekt zu beenden (oft über do_blocks() (Wenn Sie Blockinhalte verarbeiten). Tun Sie dies nur, wenn es wirklich notwendig ist, da Sie sonst die Anzahl der Sonderfälle nur vervielfachen.

Wie gelingt die richtige Internationalisierung (i18n)?

Auf der JS-Seite verwenden Sie __() und definieren textdomain in block.jsonAuf der PHP-Seite verwenden Sie __()/esc_html__()Als Nächstes generieren Sie Ihre Dateien. .po/.mo über Ihren üblichen Kanal. Dokument i18n-Block: Internationalisierung im Blockeditor.

Mein Editor stürzt mit der Fehlermeldung „Eigenschaften von undefiniert können nicht gelesen werden“ ab.

Typischerweise: ein fehlendes Attribut (falscher Name) oder ein unerwarteter Typ. Auf Konsistenz prüfen. block.json ↔ JS. Im Debug-Modus protokollieren attributes in edit() und schauen Sie sich den tatsächlichen Zustand an.

Können wir eine REST-API hinzufügen, um den Block zu speisen?

Ja, aber achte darauf, dass es sicher ist. register_rest_route() mit permission_callbackÜberprüfen Sie die Einstellungen und verwalten Sie die Funktionen. Dokument: Hinzufügen benutzerdefinierter REST-API-Endpunkte.

Wie kann ich diesen Code richtig testen?

Ich teste es in drei Durchgängen:

  • Einheit/Integration Mindestens ein PHP-Test des Renderings (render()) mit ungültigen Attributen (unbekanntes Level, HTML im Titel usw.).
  • E2E : Artikel erstellen, Block einfügen, veröffentlichen, HTML + CSS prüfen.
  • Kompat Aktivieren Sie ein „umfangreiches“ Theme (Avada/Divi) auf einer Testumgebung und prüfen Sie, ob Ihre Klassen nicht überschrieben werden.

Wo kann ich Änderungen nachverfolgen, die sich auf meine Blöcke auswirken könnten?

Im Gutenberg-Repository (GitHub) und auf Core Trac (LampenfieberWenn eine Änderung in den WordPress-Kern übernommen wird, werden hier veraltete Funktionen und API-Änderungen angezeigt.