Внедрение блочного редактора Gutenberg кардинально изменило подход к созданию контента в WordPress. Вместо единого большого текстового поля мы теперь работаем с набором независимых блоков. Хотя стандартный набор блоков достаточно широк, часто возникает необходимость в создании уникальных, специфических элементов, полностью соответствующих дизайну и функциональным требованиям проекта. В этом руководстве мы рассмотрим процесс разработки собственных блоков для Gutenberg.
Введение в разработку блоков WordPress
Что такое Gutenberg и блочный редактор WordPress?
Gutenberg — это современный редактор контента WordPress, появившийся в ядре с версии 5.0. Он заменил классический редактор WYSIWYG на блочную модель. Каждый абзац, изображение, заголовок, список и даже более сложные элементы, такие как галереи или встраивания сторонних сервисов, представлены в виде отдельных блоков. Эта структура обеспечивает большую гибкость и наглядность при создании сложных макетов страниц и записей.
Блочный редактор — это, по сути, пользовательский интерфейс, который позволяет добавлять, располагать и настраивать эти блоки. Разработка собственных блоков расширяет возможности редактора, позволяя создавать кастомные элементы контента, идеально вписывающиеся в специфику проекта.
Преимущества создания собственных блоков
Полный контроль над разметкой и стилями: Вы можете генерировать абсолютно любую HTML-структуру и применять специфические CSS-классы, что невозможно с универсальными блоками.
Интеграция со специфической логикой: Блоки могут взаимодействовать с данными плагинов, тем или внешних API, отображая динамический контент.
Улучшенный пользовательский опыт редактирования: Можно создать интуитивно понятный интерфейс для ввода и настройки сложного контента, скрыв технические детали от контент-менеджеров.
Повторное использование кода: Разработанные блоки могут использоваться на разных страницах и в разных проектах, что повышает эффективность разработки.
Соответствие брендингу: Пользовательские блоки позволяют строго следовать корпоративному стилю и дизайну.
Необходимые инструменты и навыки
Для эффективной разработки блоков потребуются следующие знания и инструменты:
JavaScript (ES6+): Gutenberg blocks преимущественно пишутся на современном JavaScript.
React (основы): Интерфейс блоков в редакторе строится с использованием React-компонентов. Понимание JSX, props, state и хуков (useState, useEffect) будет полезно.
Node.js и npm/yarn: Необходимы для сборки JavaScript и CSS ресурсов блока с помощью стандартных инструментов WordPress.
PHP: Требуется для регистрации блоков на стороне сервера, работы с динамическими блоками, метаданными и интеграции с ядром WordPress.
HTML и CSS: Базовые знания для определения структуры и внешнего вида блока.
Инструменты сборки: Знакомство с @wordpress/scripts или Webpack.
Подготовка к разработке блока
Создание пользовательского блока — это, как правило, часть более крупной задачи, интегрированной в тему или, что более предпочтительно, в отдельный плагин. Размещение блоков в плагине делает их независимыми от активной темы и позволяет легко переносить их между проектами.
Создание плагина для пользовательских блоков
Начните с создания базовой структуры плагина. В корневой папке плагина (my-custom-blocks) понадобится главный PHP файл (my-custom-blocks.php). Этот файл будет регистрировать наш блок на серверной стороне.
Минимальное содержимое my-custom-blocks.php:
<?php
/**
* Plugin Name: My Custom Blocks
* Description: A plugin to register custom Gutenberg blocks.
* Version: 1.0.0
* Author: Your Name
* License: GPL-2.0-or-later
* Text Domain: my-custom-blocks
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Registers the block using the metadata loaded from the `block.json` file.
* Behind the scenes, it registers the block using `register_block_type`.
*/
function my_custom_blocks_register_blocks() {
// Automatically register all blocks in the 'build' directory.
// Assumes block.json is present in block subdirectories within 'build'.
register_block_type( __DIR__ . '/build/my-first-block' );
// Add more blocks as needed:
// register_block_type( __DIR__ . '/build/another-block' );
}
add_action( 'init', 'my_custom_blocks_register_blocks' );
Эта функция my_custom_blocks_register_blocks будет вызвана при инициализации WordPress и зарегистрирует блоки. В данном примере используется подход с block.json и авто-регистрацией из папки build. Мы создадим структуру для my-first-block далее.
Структура файлов плагина
Рекомендуемая структура файлов для плагина с блоками (используя @wordpress/scripts):
my-custom-blocks/
├── my-custom-blocks.php
├── package.json
├── webpack.config.js (optional, for custom config)
└── src/
└── my-first-block/
├── block.json
├── editor.scss
├── index.js
└── style.scss
└── another-block/ (for additional blocks)
└── ...
my-custom-blocks.php: Основной файл плагина.
package.json: Файл для управления зависимостями npm и скриптами сборки.
src/: Папка с исходным кодом на JavaScript, JSX, SCSS.
src/my-first-block/: Папка для отдельного блока. Каждый блок должен быть в своей папке.
block.json: Метаданные блока (тип, заголовок, описание, иконка, атрибуты и др.). Появился с WP 5.8 и является предпочтительным способом регистрации блоков.
index.js: Главный JavaScript файл блока, содержащий логику edit и save.
editor.scss: Стили, применяемые только в редакторе.
style.scss: Стили, применяемые как в редакторе, так и на фронтенде.
build/: Папка, куда помещаются скомпилированные файлы (JavaScript, CSS) после запуска @wordpress/scripts.
Настройка окружения разработки (Node.js, npm)
Убедитесь, что у вас установлены Node.js и npm (или yarn). Затем, в корневой папке вашего плагина (my-custom-blocks), установите зависимости WordPress для сборки:
npm init -y # Если package.json еще нет
npm install --save-dev @wordpress/scripts
Добавьте в package.json скрипты для сборки:
{
...
"scripts": {
"build": "wp-scripts build",
"start": "wp-scripts start"
},
...
}
npm start: Запускает сборку в режиме отслеживания изменений (development mode).
npm build: Запускает сборку для продакшена (production mode).
Запуск npm start или npm build скомпилирует файлы из src/ в build/.
Создание базового блока с помощью JavaScript
Разработка JavaScript части блока начинается в файле src/my-first-block/index.js. Этот файл будет содержать логику блока.
Регистрация блока с помощью `registerBlockType`
В прошлом блоки регистрировались полностью в JavaScript. Теперь, с появлением block.json, JS-файл (index.js) фокусируется на логике edit и save. Регистрация метаданных происходит в block.json, а PHP функция register_block_type (как показано выше) автоматически считывает этот файл и регистрирует блок. В index.js мы просто импортируем регистрационную функцию из @wordpress/blocks и метаданные из block.json, а затем вызываем ее:
/**
* Registers a new block provided a unique name and an object defining its behavior.
* @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/
*/
import { registerBlockType } from '@wordpress/blocks';
/**
* Lets webpack process CSS, Sass or Less files so they can be included in the CSS bundle.
* @see https://developer.wordpress.org/block-editor/tutorials/fundamentals/styles/
*/
import './editor.scss';
import './style.scss';
/**
* Internal dependencies
*/
import Edit from './edit';
import Save from './save';
import metadata from './block.json'; // Import metadata from block.json
registerBlockType( metadata.name, {
/**
* @see ./edit.js
*/
edit: Edit,
/**
* @see ./save.js
*/
save: Save,
} );
Здесь metadata.name берется из файла block.json, который выглядит примерно так:
{
"name": "my-custom-blocks/my-first-block",
"apiVersion": 2,
"title": "My First Block",
"category": "widgets",
"icon": "smiley",
"description": "Example block with text input.",
"supports": {
"html": false,
"anchor": true
},
"textdomain": "my-custom-blocks",
"editorScript": "file:./index.js",
"editorStyle": "file:./index.css",
"style": "file:./style.css",
"attributes": {
"content": {
"type": "string",
"source": "html",
"selector": "p"
},
"alignment": {
"type": "string",
"default": ""
}
}
}
Файл block.json содержит все статические метаданные о блоке, включая определение атрибутов.
Определение атрибутов блока (attributes)
Атрибуты — это кусочки данных, которые блок хранит и использует. Они определяются в файле block.json в свойстве attributes. Каждый атрибут имеет имя, тип данных (string, boolean, number, array, object, null) и, опционально, источник (source), который указывает, как извлечь значение атрибута из сохраненного HTML блока (html, attribute, text, query, meta). Также можно задать значение по умолчанию (default).
Пример атрибута для простого текста:
"attributes": {
"message": {
"type": "string",
"default": "Hello World",
"source": "text",
"selector": "p"
}
}
Здесь мы определяем атрибут message строкового типа. Его значение по умолчанию — "Hello World", и при сохранении блока оно будет извлечено из текстового содержимого HTML-элемента <p>. В редакторе значение атрибута будет доступно через props.attributes.message.
Создание функции редактирования (edit)
Функция edit отвечает за то, как блок выглядит и ведет себя внутри редактора WordPress. Она получает объект props в качестве аргумента, который содержит атрибуты блока (props.attributes), функции для их обновления (props.setAttributes), информацию о выделении блока (props.isSelected) и другие полезные данные.
Функция edit должна возвращать JSX, описывающий интерфейс редактирования. Используются компоненты из @wordpress/element (React) и @wordpress/block-editor (специфичные для блоков компоненты, такие как RichText, InspectorControls, AlignmentControl).
Пример файла src/my-first-block/edit.js:
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n'; // For internationalization
import { useBlockProps, RichText } from '@wordpress/block-editor'; // Gutenberg editor components
/**
* Edit function for the block.
* @param {Object} props - Block properties.
* @param {Object} props.attributes - Block attributes.
* @param {Function} props.setAttributes - Function to update attributes.
* @returns {JSX.Element} JSX representing the block in the editor.
*/
export default function Edit( { attributes, setAttributes } ) {
const blockProps = useBlockProps(); // Provides default ARIA attributes and class names
const { content } = attributes; // Destructure content attribute
// Handler function for RichText content changes
const onChangeContent = ( newContent: string ): void => {
setAttributes( { content: newContent } );
};
return (
);
}
В этом примере используется компонент RichText для создания редактируемого текстового поля. При изменении текста вызывается onChangeContent, который обновляет атрибут content с помощью setAttributes. Хук useBlockProps добавляет необходимые классы и атрибуты к корневому элементу блока.
Создание функции сохранения (save)
Функция save определяет, какой HTML-код будет сохранен в базе данных при сохранении записи или страницы. Этот HTML затем отображается на фронтенде. Функция save, как и edit, получает объект props с атрибутами.
Важно, чтобы структура HTML, генерируемая функцией save, строго соответствовала тому, что ожидают источники (source) атрибутов, определенные в block.json. Это позволяет Gutenberg парсить сохраненный HTML и восстанавливать состояние атрибутов при повторном открытии редактора.
Пример файла src/my-first-block/save.js:
/**
* WordPress dependencies
*/
import { useBlockProps, RichText } from '@wordpress/block-editor';
/**
* Save function for the block.
* @param {Object} props - Block properties.
* @param {Object} props.attributes - Block attributes.
* @returns {JSX.Element|null} JSX representing the saved block output, or null for dynamic blocks.
*/
export default function Save( { attributes } ) {
const blockProps = useBlockProps.save(); // Provides default class names for save output
const { content } = attributes;
return (
);
}
Здесь мы используем RichText.Content для вывода сохраненного текста в <p> теге, что соответствует определению атрибута content в block.json (source: "html", selector: "p"). useBlockProps.save() добавляет классы, специфичные для сохраненной разметки блока.
Настройка и стилизация блока
После создания базовой структуры и логики блока, переходим к его внешнему виду и дополнительным настройкам.
Добавление CSS стилей для блока
Стили блока разделяются на два типа:
Стили редактора: Применяются только при просмотре блока в редакторе. Они помогают придать блоку в редакторе вид, максимально приближенный к фронтенду, или добавить визуальные индикаторы, отсутствующие на фронтенде (например, рамки для пустых блоков).
Файл: src/my-first-block/editor.scss
Подключается в block.json через editorStyle.
Стили фронтенда: Применяются как в редакторе, так и на сайте для посетителей. Это основные стили блока.
Файл: src/my-first-block/style.scss
Подключается в block.json через style.
Стили пишутся с использованием специфических CSS классов, которые WordPress автоматически добавляет к корневому элементу вашего блока. Основной класс имеет формат .wp-block-[namespace]-[block-name], например, .wp-block-my-custom-blocks-my-first-block.
Пример src/my-first-block/style.scss:
.wp-block-my-custom-blocks-my-first-block {
background-color: #f0f0f0;
border: 1px solid #ccc;
padding: 15px;
margin-bottom: 20px;
p {
font-size: 16px;
line-height: 1.5;
}
}
Пример src/my-first-block/editor.scss (добавление рамки в редакторе)::
.editor-styles-wrapper .wp-block-my-custom-blocks-my-first-block {
border: 2px dashed #ff0000;
// Дополнительные стили только для редактора
}
Обратите внимание на использование .editor-styles-wrapper для стилей редактора, чтобы обеспечить их правильное применение в изолированной среде iframe.
Использование компонентов WordPress для интерфейса блока
WordPress предоставляет набор готовых React-компонентов для создания стандартных элементов управления и отображения в редакторе. Они находятся в пакетах @wordpress/components и @wordpress/block-editor. Использование этих компонентов обеспечивает единообразие интерфейса и доступность.
Примеры компонентов:
PanelBody, PanelRow: Для создания секций и строк в боковой панели.
TextControl, TextareaControl: Текстовые поля.
SelectControl: Выпадающие списки.
ToggleControl: Переключатели (чекбоксы).
MediaUpload: Загрузка изображений или других медиафайлов.
RichText: Редактируемое текстовое поле (уже использовалось).
InspectorControls: Компонент-обертка для элементов управления, которые должны отображаться в боковой панели настроек блока.
BlockControls: Компонент-обертка для элементов управления, которые должны отображаться на тулбаре блока.
Добавление элементов управления в редактор блока (InspectorControls)
Часто блоку нужны настройки, которые не являются частью основного контента, но влияют на его внешний вид или поведение (например, цвет фона, отступы, выбор опции). Эти настройки обычно размещаются в боковой панели редактора с помощью компонента InspectorControls.
Добавим в наш edit.js настройку выравнивания текста:
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { useBlockProps, RichText, InspectorControls } from '@wordpress/block-editor';
import { PanelBody, SelectControl } from '@wordpress/components';
/**
* Edit function for the block.
* @param {Object} props - Block properties.
* @param {Object} props.attributes - Block attributes.
* @param {Function} props.setAttributes - Function to update attributes.
* @returns {JSX.Element} JSX representing the block in the editor.
*/
export default function Edit( { attributes, setAttributes } ) {
const blockProps = useBlockProps();
const { content, alignment } = attributes; // Destructure alignment attribute
// Handler for RichText content changes
const onChangeContent = ( newContent: string ): void => {
setAttributes( { content: newContent } );
};
// Handler for Alignment select changes
const onChangeAlignment = ( newAlignment: string | undefined ): void => {
setAttributes( { alignment: newAlignment } );
};
return (
{/* Apply alignment style */}
{/* Inspector Controls for sidebar settings */}
);
}
И, соответственно, обновим save.js для сохранения этого атрибута (например, как inline стиль):
/**
* WordPress dependencies
*/
import { useBlockProps, RichText } from '@wordpress/block-editor';
/**
* Save function for the block.
* @param {Object} props - Block properties.
* @param {Object} props.attributes - Block attributes.
* @returns {JSX.Element|null} JSX representing the saved block output, or null for dynamic blocks.
*/
export default function Save( { attributes } ) {
const { content, alignment } = attributes;
// Apply alignment style to block props if alignment is set
const blockProps = useBlockProps.save( { style: { textAlign: alignment } } );
return (
);
}
Теперь в боковой панели редактора появится секция "Block Settings" с выпадающим списком для выбора выравнивания.
Использование динамических блоков с PHP
В большинстве случаев статические блоки, где save генерирует финальный HTML, подходят хорошо. Однако, если содержимое блока:
часто меняется (например, список последних постов);
зависит от серверной логики или данных, недоступных в JavaScript редактора;
содержит сложную PHP-логику;
то лучше использовать динамические блоки. Динамический блок не сохраняет свой вывод в post_content. Вместо этого функция save возвращает null, а рендеринг блока происходит на стороне сервера при отображении страницы с помощью PHP-функции обратного вызова (render_callback).
В block.json для динамического блока save не указывается, или его можно указать как null в старом формате. В PHP регистрации (register_block_type), добавляется аргумент render_callback:
// In my-custom-blocks.php
function my_custom_blocks_register_blocks() {
// Register static block (as before)
register_block_type( __DIR__ . '/build/my-first-block' );
// Register a dynamic block (example: recent posts)
register_block_type( __DIR__ . '/build/my-dynamic-block', [
'render_callback' => 'my_custom_blocks_render_recent_posts_block',
] );
}
add_action( 'init', 'my_custom_blocks_register_blocks' );
/**
* Server-side rendering for the dynamic recent posts block.
* @param array $attributes The block attributes.
* @param string $content The block inner content.
* @return string The rendered HTML.
*/
function my_custom_blocks_render_recent_posts_block( array $attributes, string $content ): string {
$args = [
'posts_per_page' => $attributes['numberOfItems'] ?? 5, // Use an attribute for count
'post_status' => 'publish',
'ignore_sticky_posts' => true,
];
$recent_posts = new WP_Query( $args );
if ( ! $recent_posts->have_posts() ) {
return '' . __( 'No recent posts found.', 'my-custom-blocks' ) . '';
}
$output = '';
while ( $recent_posts->have_posts() ) {
$recent_posts->the_post();
$output .= sprintf(
'- %s
',
escape_url( get_the_permalink() ),
escape_html( get_the_title() )
);
}
$output .= '
';
wp_reset_postdata();
return $output;
}
В этом примере блок my-dynamic-block (который также должен иметь block.json, index.js, editor.scss, style.scss) не сохраняет список постов в HTML. Вместо этого, когда WordPress отображает страницу, вызывается PHP функция my_custom_blocks_render_recent_posts_block, которая выполняет запрос к базе данных и генерирует актуальный список постов.
Продвинутые техники и оптимизация
После освоения основ, можно рассмотреть более сложные сценарии и методы для улучшения пользовательских блоков.
Работа с ACF (Advanced Custom Fields) и Gutenberg
Плагин Advanced Custom Fields (ACF) предоставляет удобный способ создания метаполей для записей и страниц. ACF Pro также предлагает функцию создания блоков на основе PHP шаблонов, что может быть проще для разработчиков с сильным PHP бэкграундом и ограниченным знанием React/JavaScript. Эти блоки используют PHP шаблоны для рендеринга как в редакторе, так и на фронтенде, а интерфейс редактирования строится на основе полей ACF.
Также можно синхронизировать атрибуты вашего нативного Gutenberg блока с полями ACF, используя метаданные поста (source: "meta" в block.json), что позволяет использовать ACF для управления данными, а Gutenberg — для их представления и редактирования.
Локализация блока для разных языков
Для создания многоязычных блоков необходимо использовать функции интернационализации WordPress. В JavaScript используйте wp.i18n.__() для перевода строк. Убедитесь, что домен перевода (textdomain) совпадает с тем, что указан в block.json и заголовке основного PHP файла плагина.
// In edit.js or other JS file
import { __ } from '@wordpress/i18n';
// ... later in your code
{ __( 'This text will be translated.', 'my-custom-blocks' ) }
На стороне PHP используйте стандартные функции, такие как __(), _e(), sprintf(), после загрузки текстового домена плагина с помощью load_plugin_textdomain().
// In my-custom-blocks.php or render_callback
load_plugin_textdomain( 'my-custom-blocks', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' );
// ... later in your code
echo '' . __( 'Some text to translate.', 'my-custom-blocks' ) . '';
После добавления всех строк, используйте инструменты вроде WP-CLI (wp i18n make-pot) для создания PO/POT файлов, которые затем переводятся и компилируются в MO/JSON файлы.
Оптимизация производительности пользовательских блоков
Неоптимизированные блоки могут замедлять работу редактора и фронтенда. Вот несколько советов:
Оптимизируйте edit функцию: Избегайте выполнения тяжелых операций или запросов данных непосредственно в edit. Используйте хуки useSelect (для получения данных из хранилища WordPress) или useEffect с очисткой для асинхронных операций.
Используйте useBlockProps: Он оптимизирован для применения атрибутов и классов к корневому элементу блока.
Динамические блоки для часто меняющихся данных: Не сохраняйте в post_content данные, которые быстро устаревают (например, ленту новостей). Используйте render_callback.
Оптимизируйте запросы в render_callback: Если ваш динамический блок делает запросы к базе данных или внешним API, убедитесь, что они эффективны и, возможно, кешируются.
Условная загрузка скриптов и стилей: Загружайте JS и CSS только для тех блоков, которые присутствуют на странице (это часто делается автоматически при использовании block.json и register_block_type).
Минимизируйте размер бандлов: @wordpress/scripts делает это по умолчанию в production сборке (npm build).
Публикация и распространение вашего блока
Если ваш блок предназначен для многократного использования или для распространения среди других пользователей WordPress, рекомендуется оформить его как полноценный плагин.
Шаги для публикации на WordPress.org:
Убедитесь, что ваш код соответствует стандартам кодирования WordPress (PHP, JS, CSS).
Создайте файл readme.txt в формате стандартов WordPress.org.
Убедитесь, что в коде используются функции локализации (__, _e и т.д.).
Протестируйте блок в различных сценариях и на разных версиях WordPress.
Запакуйте плагин (кроме .git, node_modules, src и других временных файлов).
Загрузите плагин в репозиторий WordPress.org через SVN или Git (если включена поддержка Git).
Собственные блоки открывают огромные возможности для создания уникального и гибкого контента в WordPress. Изучение Block Editor Handbook и эксперименты с компонентами и API — лучший способ углубить свои знания в этой области.