LW IT Solutions logo LW IT Solutions logo LW IT Solutions
« Przegląd bloga /Wtyczki i triki WordPress/Tutorials / Dynamiczny blok Gutenberga z block.json i render.php
Ten artykuł w innych językach:

Dynamiczny blok Gutenberga z block.json i render.php

Dynamiczny blok Gutenberga z block.json i render.php
Spis treści
  1. Co dziś generuje szkielet create-block
  2. block.json jako umowa między edytorem a serwerem
  3. Rejestracja przez manifest zamiast pojedynczych plików block.json
  4. render.php i jego trzy zmienne
  5. Strona edytora: edit.js z ServerSideRender
  6. Statycznie czy dynamicznie: co trafia do bazy danych
  7. Ograniczenia i otwarte kwestie
  8. Pytania i odpowiedzi
  9. Źródła

Blok, którego wynik zależy od danych zmieniających się już po zapisaniu wpisu (najnowsze wpisy, cena, lista wydarzeń), nie może przechowywać gotowego HTML w post_content. Każdy nowy wpis w kategorii sprawiałby, że zapisany kod się zdezaktualizuje. Bloki statyczne mają też drugą słabość: gdy zmienia się kod zwracany przez funkcję save, starsze wpisy przestają do niego pasować, edytor zgłasza nieprawidłową treść, a blok potrzebuje wpisu w tablicy deprecations. Blok dynamiczny omija oba problemy. Edytor zapisuje wyłącznie komentarz bloku z atrybutami, a kod HTML generuje PHP przy każdym żądaniu.

Artykuł pokazuje, jak od podstaw zbudować listę najnowszych wpisów z jednej kategorii z atrybutami categoryId i count. Wyjaśnia, co generuje @wordpress/create-block według stanu na 29.09.2026, jak block.json łączy edytor z serwerem, dlaczego szkielet rejestruje dziś bloki przez wygenerowany manifest i jakie zmienne otrzymuje render.php. Kod jest kompletny; wtyczka działa od WordPressa 6.9, a w chwili pisania aktualnym wydaniem jest 7.1.2.

Co dziś generuje szkielet create-block

Oficjalnym narzędziem jest pakiet npm @wordpress/create-block; w dniu pobrania najnowszą opublikowaną wersją jest 4.99.0 z 23.09.2026. Wymagany jest Node.js w wersji 20.10.0 lub nowszej. Standardowy szablon zna dwa warianty, static i dynamic. Wariant dynamiczny różni się dokładnie w trzech miejscach: block.json dostaje pole render wskazujące na render.php, index.js rejestruje blok bez funkcji save, a skrypty npm kopiują pliki PHP do katalogu build. Dla przykładu polecenia wyglądają następująco:

Shell
npx @wordpress/create-block@latest category-posts --variant dynamic --namespace lw --title "Category Posts"
cd category-posts
npm install @wordpress/components @wordpress/core-data @wordpress/data @wordpress/html-entities @wordpress/server-side-render --save
npm start

Dodatkowa linia z npm install nie jest konieczna do samego budowania, ponieważ @wordpress/scripts traktuje importy z @wordpress/* jako zależności zewnętrzne i mapuje je na uchwyty skryptów, które WordPress i tak dostarcza. Zainstalowane pakiety pozwalają jednak narzędziom edytora i linterowi rozpoznać importy. npm start obserwuje src/ i zapisuje wynik w build/, a npm run build tworzy zminimalizowaną wersję do publikacji. Katalog wtyczki wygląda potem mniej więcej tak:

Tekst
category-posts/                 (abridged)
├── category-posts.php          plugin header and block registration
├── package.json                build/start scripts with --blocks-manifest
├── readme.txt
├── src/
│   └── category-posts/
│       ├── block.json
│       ├── index.js
│       ├── edit.js
│       ├── render.php
│       ├── view.js             not needed here, deleted
│       ├── style.scss
│       └── editor.scss
└── build/                      written by npm start / npm run build
    ├── blocks-manifest.php
    └── category-posts/
        ├── block.json
        ├── index.js
        ├── index.asset.php
        ├── index.css
        ├── style-index.css
        └── render.php

W standardowym szablonie blok znajduje się w src/<slug>/, a nie bezpośrednio w src/. Text domain domyślnie odpowiada slugowi, tutaj category-posts. Szkielet tworzy też plik view.js wraz z wpisem viewScript dla JavaScriptu na froncie. Lista renderowana po stronie serwera, bez interakcji, tego nie potrzebuje, dlatego przykład usuwa oba elementy. Dwa skrypty npm istotne dla bloku dynamicznego szkielet zapisuje tak:

JSON
{
	"scripts": {
		"build": "wp-scripts build --webpack-copy-php --blocks-manifest",
		"start": "wp-scripts start --webpack-copy-php --blocks-manifest"
	}
}

--webpack-copy-php kopiuje wszystkie pliki PHP z src do build; bez tej flagi @wordpress/scripts przenosi tylko pliki PHP wskazane w block.json w polach render lub variations. --blocks-manifest zapisuje plik build/blocks-manifest.php, o którym mowa w kolejnych sekcjach. Wygenerowany nagłówek wtyczki podaje „Requires at least: 6.8”, a readme.txt „Tested up to: 6.8”. To wartości domyślne szablonu, a nie informacja o bieżącym wydaniu, i przed publikacją należy je dostosować. Pola nagłówka omawia osobny artykuł z tej serii, poświęcony readme.txt, nagłówkowi wtyczki i SVN.

block.json jako umowa między edytorem a serwerem

block.json jest czytany po obu stronach. PHP rejestruje na jego podstawie typ bloku, atrybuty i zasoby; pakiet edytora importuje ten sam plik, żeby JavaScript znał nazwę i schemat atrybutów. W przykładzie plik w src/category-posts/ ma następującą treść:

JSON
{
	"$schema": "https://schemas.wp.org/trunk/block.json",
	"apiVersion": 3,
	"name": "lw/category-posts",
	"version": "0.1.0",
	"title": "Category Posts",
	"category": "widgets",
	"icon": "list-view",
	"description": "Lists the latest posts of one category, rendered on the server.",
	"attributes": {
		"categoryId": {
			"type": "integer",
			"default": 0
		},
		"count": {
			"type": "integer",
			"default": 5
		}
	},
	"usesContext": [ "postId" ],
	"supports": {
		"html": false,
		"align": [ "wide", "full" ],
		"color": {
			"background": true,
			"text": true,
			"link": true
		},
		"spacing": {
			"margin": true,
			"padding": true
		},
		"typography": {
			"fontSize": true
		}
	},
	"textdomain": "category-posts",
	"editorScript": "file:./index.js",
	"editorStyle": "file:./index.css",
	"style": "file:./style-index.css",
	"render": "file:./render.php"
}

apiVersion 3 to aktualna wersja Block API, wprowadzona w WordPressie 6.3. Gdy pola brakuje, WordPress przyjmuje wersję 1. Wersja 3 wiąże się z edytorem w iframe: od 6.3 edytor wpisów używał iframe tylko wtedy, gdy wszystkie zarejestrowane bloki deklarowały wersję 3 lub wyższą. WordPress 7.0 zmienił kryterium na bloki faktycznie obecne w treści wpisu, a według dokumentacji Block API WordPress 7.1 zawsze używa iframe dla edytora wpisów, niezależnie od apiVersion. Dla nowego bloku wynika z tego jedna konsekwencja: style edytora muszą działać wewnątrz iframe, co jest spełnione, gdy ładują się przez editorStyle i style w block.json.

Oba atrybuty mają typ integer i wartość domyślną. Przed renderowaniem WordPress sprawdza każdy atrybut względem schematu, odrzuca wartości niezgodne ze schematem i uzupełnia brakujące wartościami domyślnymi. usesContext żąda kontekstu postId, który render_block() wypełnia na podstawie globalnego wpisu. Dzięki temu blok może pominąć wpis, w którym sam się znajduje.

Sekcja supports włącza wyrównanie, kolory, odstępy i rozmiar czcionki. HTML dla tych ustawień generują później get_block_wrapper_attributes() i useBlockProps(), więc nie potrzeba własnego kodu. editorScript i editorStyle są ładowane tylko w edytorze, a style w edytorze i na stronie. render jest polem wyłącznie serwerowym.

Rejestracja przez manifest zamiast pojedynczych plików block.json

Przez długi czas typową rejestracją było register_block_type( __DIR__ . '/build' ) albo ścieżka do katalogu bloku. To nadal działa i sprawdza się przy pojedynczym bloku bez etapu budowania. Przy każdym żądaniu wymaga jednak odczytu pliku JSON z dysku i jego dekodowania. WordPress 6.7 dodał wp_register_block_metadata_collection(), która rejestruje manifest PHP z metadanymi wielu bloków; notatka deweloperska Felixa Arntza jako powód podaje uniknięcie operacji na systemie plików i dekodowania JSON. W 6.7 każdy blok nadal potrzebował osobnego wywołania register_block_type(). WordPress 6.8 zamknął tę lukę funkcją wp_register_block_types_from_metadata_collection(): jedno wywołanie rejestruje kolekcję i wszystkie zawarte w niej typy bloków.

Obecny szkielet używa właśnie tej funkcji, gdy włączony jest @wordpress/scripts, co jest ustawieniem domyślnym. Główny plik wtyczki w przykładzie wygląda więc tak:

PHP
<?php
/**
 * Plugin Name:       Category Posts
 * Description:       Server-rendered block that lists the latest posts of one category.
 * Version:           0.1.0
 * Requires at least: 6.9
 * Requires PHP:      7.4
 * Author:            Lukas Wojcik
 * License:           GPL-2.0-or-later
 * License URI:       https://www.gnu.org/licenses/gpl-2.0.html
 * Text Domain:       category-posts
 *
 * @package Lw
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly.
}

/**
 * Registers every block listed in build/blocks-manifest.php.
 *
 * The manifest is generated by wp-scripts (--blocks-manifest), so WordPress
 * does not have to read and decode each block.json on every request.
 */
function lw_category_posts_block_init() {
	wp_register_block_types_from_metadata_collection(
		__DIR__ . '/build',
		__DIR__ . '/build/blocks-manifest.php'
	);
}
add_action( 'init', 'lw_category_posts_block_init' );

Pierwszy parametr to katalog bazowy kolekcji, drugi to manifest. Jeśli drugi zostanie pusty, katalog bazowy musi wskazywać kolekcję zarejestrowaną wcześniej. Wtyczki obsługujące jeszcze WordPressa 6.7 lub starszego potrzebują sprawdzenia function_exists() i ścieżki zapasowej, opisanej w notatce do 6.8. Przykład unika tego dzięki „Requires at least: 6.9”, czego i tak wymaga pokazany niżej import w edytorze. Bez etapu budowania wystarcza pojedyncze wywołanie:

Tekst
// Single block, without a manifest: reads build/category-posts/block.json.
register_block_type( __DIR__ . '/build/category-posts' );

Łatwo przeoczyć jeden szczegół: manifest jest produktem budowania. Zmieniony w src/ plik block.json zaczyna obowiązywać dopiero po kolejnym buildzie, także po stronie serwera.

render.php i jego trzy zmienne

Gdy block.json zawiera pole render, WordPress przy rejestracji tworzy render_callback. W kodzie źródłowym rdzenia jest to statyczna closure, która uruchamia buforowanie wyjścia, wczytuje plik przez require i zwraca zawartość bufora. W pliku dostępne są trzy zmienne: $attributes (tablica atrybutów bloku z uzupełnionymi wartościami domyślnymi), $content (zapisana treść wewnętrzna, istotna dla bloków z blokami zagnieżdżonymi, tutaj zawsze pusta) oraz $block (instancja WP_Block z nazwą, kontekstem i sparsowanym blokiem). Plik renderujący w przykładzie:

PHP
<?php
/**
 * Server-side rendering of the lw/category-posts block.
 *
 * Variables provided by WordPress:
 *
 * @var array    $attributes Block attributes, defaults from block.json applied.
 * @var string   $content    Inner content (always empty for this block).
 * @var WP_Block $block      Block instance: name, context, parsed block.
 *
 * @package Lw
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly.
}

$lw_category_id = isset( $attributes['categoryId'] ) ? absint( $attributes['categoryId'] ) : 0;
$lw_count       = isset( $attributes['count'] ) ? absint( $attributes['count'] ) : 5;
$lw_count       = min( max( $lw_count, 1 ), 10 );
$lw_term        = $lw_category_id ? get_term( $lw_category_id, 'category' ) : null;

// Render nothing until a valid category has been chosen.
if ( ! $lw_term instanceof WP_Term ) {
	return;
}

// Do not list the post that contains the block (context from "usesContext").
$lw_current_id = isset( $block->context['postId'] ) ? absint( $block->context['postId'] ) : 0;

$lw_posts = get_posts(
	array(
		'category'     => $lw_term->term_id,
		'numberposts'  => $lw_count,
		'post_status'  => 'publish',
		'has_password' => false,
		'exclude'      => $lw_current_id ? array( $lw_current_id ) : array(),
	)
);
?>
<div <?php echo wp_kses_data( get_block_wrapper_attributes() ); ?>>
	<?php if ( empty( $lw_posts ) ) : ?>
		<p><?php esc_html_e( 'No posts in this category yet.', 'category-posts' ); ?></p>
	<?php else : ?>
		<ul class="wp-block-lw-category-posts__list">
			<?php foreach ( $lw_posts as $lw_post ) : ?>
				<li>
					<a href="<?php echo esc_url( get_permalink( $lw_post ) ); ?>"><?php echo esc_html( get_the_title( $lw_post ) ); ?></a>
					<time datetime="<?php echo esc_attr( get_the_date( 'c', $lw_post ) ); ?>"><?php echo esc_html( get_the_date( '', $lw_post ) ); ?></time>
				</li>
			<?php endforeach; ?>
		</ul>
	<?php endif; ?>
</div>

Kilka decyzji w tym pliku wynika ze sposobu jego wczytywania. Ponieważ require wykonuje się dla każdej instancji bloku na stronie, plik nie może deklarować funkcji ani klas; druga instancja bloku zakończyłaby się błędem krytycznym. Funkcje pomocnicze należy umieszczać w głównym pliku wtyczki lub w osobnym pliku dołączanym. Dla PHP_CodeSniffera zmienne wyglądają na globalne, dlatego mają prefiks lw_. Kontroli to jednak nie zadowala: WPCS odrzuca prefiksy krótsze niż cztery znaki, a Plugin Check 2.1.0, który wyprowadza oczekiwany prefiks z kodu wtyczki (tutaj z nazwy funkcji lw_category_posts_block_init), zgłasza każdą z tych zmiennych jako ostrzeżenie. Na działanie bloku ostrzeżenia nie mają wpływu. return kończy plik wcześniej i zostawia pusty bufor, i tak właśnie blok nie renderuje niczego.

Atrybuty pochodzą z post_content, więc są danymi wejściowymi. Walidacja schematu gwarantuje liczby całkowite, ale nie rozsądny zakres, dlatego absint() razem z min() i max() ograniczają liczbę wpisów do przedziału od 1 do 10. get_term() potwierdza, że kategoria istnieje. get_posts() to cienka nakładka na WP_Query, która sama ustawia ignore_sticky_posts, no_found_rows i suppress_filters; to pasuje do krótkiej listy bez paginacji. Jak kolejne argumenty zapytania, na przykład meta_query, zamieniają się w SQL, pokazuje artykuł o meta_query w WP_Query. Każde wyjście przechodzi przez odpowiednią funkcję escapującą: esc_url() dla odnośnika, esc_html() dla tytułu i daty, esc_attr() dla atrybutu datetime. Ogólne zasady opisuje osobny artykuł z tej serii o sanityzacji, escapowaniu, Nonce i Capabilities.

get_block_wrapper_attributes() zwraca ciąg atrybutów dla elementu opakowującego: klasę wp-block-lw-category-posts oraz klasy i style inline wynikające z ustawień supports. Funkcja sama escapuje każdą wartość przez esc_attr(), co widać w jej kodzie źródłowym. Nie ma jej jednak na liście funkcji automatycznie escapujących w WordPress Coding Standards, więc gołe echo uruchamia sniff dotyczący escapowania; Plugin Check 2.1.0 zgłasza je jako błąd (WordPress.Security.EscapeOutput.OutputNotEscaped). wp_kses_data() pozostawia ciąg atrybutów bez tagów w niezmienionej postaci i zadowala tę kontrolę; alternatywą jest uzasadniony komentarz phpcs:ignore.

Strona edytora: edit.js z ServerSideRender

W JavaScripcie index.js rejestruje blok wyłącznie z edit. Gdy save nie zostanie podane, Block API używa domyślnej funkcji save, zwracającej null:

JavaScript
import { registerBlockType } from '@wordpress/blocks';
import './style.scss';
import Edit from './edit';
import metadata from './block.json';

// No save(): the default save returns null, so only the block comment
// with its attributes is stored in post_content.
registerBlockType( metadata.name, {
	edit: Edit,
} );

edit.js dostarcza ustawienia w panelu bocznym oraz podgląd. Kategorie pochodzą z magazynu danych rdzenia, z tym samym zapytaniem, którego używa blok rdzenia „Najnowsze wpisy”. Podgląd tworzy ServerSideRender: komponent wysyła atrybuty do trasy REST /wp/v2/block-renderer/lw/category-posts i wstawia HTML zwrócony przez render.php:

JavaScript
import { __ } from '@wordpress/i18n';
import { InspectorControls, useBlockProps } from '@wordpress/block-editor';
import {
	PanelBody,
	Placeholder,
	RangeControl,
	SelectControl,
	Spinner,
} from '@wordpress/components';
import { useSelect } from '@wordpress/data';
import { store as coreStore } from '@wordpress/core-data';
import { decodeEntities } from '@wordpress/html-entities';
import { ServerSideRender } from '@wordpress/server-side-render';
import metadata from './block.json';
import './editor.scss';

// Same query shape as the core Latest Posts block uses for its category list.
const CATEGORIES_QUERY = { per_page: -1, _fields: 'id,name', context: 'view' };

export default function Edit( { attributes, setAttributes } ) {
	const { categoryId, count } = attributes;
	const blockProps = useBlockProps();

	const categories = useSelect(
		( select ) =>
			select( coreStore ).getEntityRecords(
				'taxonomy',
				'category',
				CATEGORIES_QUERY
			),
		[]
	);

	const options = [
		{ value: '0', label: __( '— Select a category —', 'category-posts' ) },
		...( categories ?? [] ).map( ( term ) => ( {
			value: String( term.id ),
			label: decodeEntities( term.name ),
		} ) ),
	];

	return (
		<>
			<InspectorControls>
				<PanelBody title={ __( 'Settings', 'category-posts' ) }>
					{ categories ? (
						<SelectControl
							__next40pxDefaultSize
							__nextHasNoMarginBottom
							label={ __( 'Category', 'category-posts' ) }
							value={ String( categoryId ) }
							options={ options }
							onChange={ ( value ) =>
								setAttributes( { categoryId: Number( value ) } )
							}
						/>
					) : (
						<Spinner />
					) }
					<RangeControl
						__next40pxDefaultSize
						__nextHasNoMarginBottom
						label={ __( 'Number of posts', 'category-posts' ) }
						value={ count }
						min={ 1 }
						max={ 10 }
						onChange={ ( value ) =>
							setAttributes( { count: value ?? 5 } )
						}
					/>
				</PanelBody>
			</InspectorControls>
			<div { ...blockProps }>
				{ categoryId ? (
					<ServerSideRender
						block={ metadata.name }
						attributes={ attributes }
						skipBlockSupportAttributes
					/>
				) : (
					<Placeholder
						icon="list-view"
						label={ __( 'Category Posts', 'category-posts' ) }
						instructions={ __(
							'Choose a category in the block settings.',
							'category-posts'
						) }
					/>
				) }
			</div>
		</>
	);
}

W wersji pakietu dołączonej do WordPressa 6.9 (6.9.5) @wordpress/server-side-render eksportuje ServerSideRender już jako eksport nazwany; eksport domyślny jest oznaczony jako przestarzały i pozostaje warstwą zgodności. Ten nazwany import jest powodem wpisu „Requires at least: 6.9” w nagłówku wtyczki. skipBlockSupportAttributes usuwa z żądania atrybuty stylów, ponieważ zewnętrzny div otrzymuje kolory i odstępy już przez useBlockProps(); w przeciwnym razie pojawiłyby się podwójnie. Komponent dołącza też do żądania identyfikator bieżącego wpisu, więc kontekst postId w render.php działa również w edytorze. Dwa propsy __next… przy kontrolkach przełączają starsze wersje WordPressa na aktualny rozmiar i odstępy kontrolek; według changelogu pakietu components oba są już domyślne i da się je usunąć, gdy starsze wersje przestaną być obsługiwane.

Blok dynamiczny: który plik jest czytany, gdzie i kiedy, w trzech kolumnach dla każdego żądania PHP, edytora bloków i frontu; blocks-manifest.php rejestruje wszystkie bloki, edytor ładuje index.js z podglądem ServerSideRender, a render.php tworzy HTML z atrybutów zapisanych w post_content
PHP rejestruje z manifestu, edytor ładuje pakiet, render.php tworzy HTML dla frontu i podglądu

Diagram podsumowuje, kto i kiedy czyta poszczególne pliki: PHP czyta manifest przy każdym żądaniu, edytor ładuje pakiet skryptu i oba arkusze stylów, a render.php działa zarówno dla frontu, jak i dla podglądu w edytorze przez trasę REST.

Statycznie czy dynamicznie: co trafia do bazy danych

W przykładzie baza danych zawiera wyłącznie komentarz bloku. Atrybuty równe wartości domyślnej nie są serializowane:

Tekst
<!-- wp:lw/category-posts {"categoryId":7,"count":3} /-->

Tu leży sedno różnicy. Blok statyczny zapisuje wynik funkcji save jako HTML między ogranicznikami komentarza, a edytor przy otwieraniu wpisu porównuje ten HTML ze świeżym wywołaniem save. Każda zmiana kodu (dodatkowa klasa, element opakowujący, inny tag) psuje więc istniejące treści, dopóki tablica deprecated nie opisze starych wersji. W bloku dynamicznym save zwraca null, edytor pomija walidację kodu, a zmiana w render.php po kolejnym wdrożeniu obejmuje każde wystąpienie bloku. Podręcznik edytora bloków wymienia dokładnie te dwa przypadki dla renderowania dynamicznego: treść, która ma się zmieniać bez aktualizacji wpisu, oraz kod, który prawdopodobnie będzie się zmieniał.

Renderowanie dynamiczne wymaga dodatkowej pracy serwera: każde wyświetlenie strony z blokiem uruchamia zapytanie do bazy, a każda zmiana atrybutu w edytorze wysyła żądanie REST. Przy krótkiej liście jest to zwykle do przyjęcia. Jeśli generowanie wyniku jest kosztowne, wyrenderowany HTML można przechować w transiencie lub w cache obiektów. Żadne z tych rozwiązań nie jest częścią przykładu, a na potrzeby artykułu nie wykonano pomiarów. Blok zależy też od aktywnej wtyczki: bez niej nie renderuje niczego, ponieważ post_content nie zawiera zastępczego HTML.

Ograniczenia i otwarte kwestie

  • Dokumentacja ServerSideRender określa komponent jako mechanizm zapasowy lub przestarzały, nieodpowiedni do rozwijania nowych funkcji. Dla bloków, których podgląd da się zbudować w JavaScripcie z danych REST, zalecaną drogą jest renderowanie po stronie klienta. Przykład używa komponentu celowo, aby render.php pozostał jedynym źródłem kodu HTML; ceną jest zagnieżdżony element opakowujący w edytorze (zewnętrzny div z useBlockProps() zawiera div z render.php).
  • WordPress 7.0 wprowadził rejestrację bloków wyłącznie w PHP: z 'autoRegister' => true w supports i funkcją render_callback blok pojawia się w edytorze bez własnego JavaScriptu, a dla atrybutów typu string, integer, boolean i enum kontrolki powstają automatycznie. Wybór kategorii po nazwie zamiast numerycznego ID nie jest tym objęty, dlatego przykład zachowuje własny edit.js.
  • Opisany szkielet odpowiada wersji 4.99.0 pakietu @wordpress/create-block. Pakiet ukazuje się często; nazwy plików, flagi i wartości domyślne mogą się zmienić w kolejnych wersjach.
  • Plugin Check 2.1.0 nie zgłasza dla przykładu żadnych błędów w WordPressie 7.1.2 (sprawdzono 30.09.2026). Dla każdej kopii render.php w src/ i build/ wymienia siedem ostrzeżeń PrefixAllGlobals oraz jedno ostrzeżenie dotyczące parametru exclude w get_posts(); gołe echo get_block_wrapper_attributes() zgłasza jako błąd.
  • Cache wyrenderowanej listy i koszt podglądu przez REST nie zostały zmierzone.

Pytania i odpowiedzi

Dlaczego zmiana w block.json nie działa, choć plik został zapisany?

Rejestracja korzysta z build/blocks-manifest.php, a nie z src/. Manifest powstaje podczas budowania; PHP widzi nową wersję dopiero po npm run build lub przy działającym npm start.

Czy render.php może deklarować własne funkcje pomocnicze?

Nie. WordPress wczytuje plik przez require ponownie dla każdej instancji bloku. Przy dwóch instancjach na stronie druga deklaracja tej samej funkcji kończy się błędem krytycznym. Funkcje pomocnicze z prefiksem należy umieszczać w głównym pliku wtyczki lub w osobnym pliku dołączanym.

Czy blok renderowany na serwerze potrzebuje jeszcze JavaScriptu od WordPressa 7.0?

Niekoniecznie. Z 'autoRegister' => true w supports i funkcją render_callback blok pojawia się w edytorze bez własnego JavaScriptu, a kontrolki powstają dla atrybutów typu string, integer, boolean i enum. Lista wyboru z nazwami kategorii lub własny placeholder nadal wymagają pliku edit.js.

Lukas Wójcik

Lukas Wójcik

Systems architect and technology enthusiast specializing in scalable tracking solutions, GMP Stack (GA4 & GTM), and robust backend architectures. Advocate for clean code and privacy-first design.

Kontakt

Masz pytanie lub pomysł na projekt internetowy? Napisz krótko, o co chodzi. Formularz korzysta z reCAPTCHA do ochrony przed spamem.

Napisanie komentarza

Doświadczenia z innymi wtyczkami lub środowiskami hostingowymi oraz pytania o konfigurację są tu mile widziane.

Adres e-mail nie jest publikowany. Pola obowiązkowe oznaczono gwiazdką.