В блочном редакторе WordPress (предыдущее название Gutenberg) есть новый способ добавления контента в ваши WordPress посты, страницы, а в скором времени, и в любое содержимое вашего сайта.
Это означает движение WordPress в сторону пейджбилдера (конструктор страниц).
Базовый набор блоков по умолчанию довольно хорош, но что если вы захотите создать собственный кастомный блок для контента? В прошлом вы использовали что-то вроде расширенных пользовательских полей (ACF) или шорткоды. Сейчас пользовательские блоки — это то, что вам надо.
В прошлом году я целыми днями работал с React, перестраивая фронтэнд для WP Migrate DB Pro. React засел у меня в уме, и учитывая то, что WordPress написан на React, я подумал, было бы неплохо посмотреть, что нужно для создания пользовательского блока. Итак, давайте разберемся!
Введени в блоки Gutenberg
Мы собираемся выяснить, что требуется для перехода от «ничто» к чему-то относительно простому, к кастомному блоку Gutenberg. Документации по редактору блоков, вроде как, навалом повсеместно, при этом в «Справочнике по редактору блоков» также имеется кое-какая достойная информация. К сожалению, она не так легка для анализа. Поэтому сделаем несколько шагов, которые сделал я, чтобы приблизиться к ее пониманию.
Прежде чем приступить к созданию блока Gutenberg, я рекомендую, по крайней мере, на данный момент, иметь четкое понимание React и современного JavaScript. Если вы не знакомы с такими вещами, как JSX, можете использовать синтаксис ES5, но для использования в долгосрочной перспективе лучше подходит современный синтаксис.
Сейчас довольно просто выполнять настройку с помощью команды WP CLI «scaffold». Она создаст тему или плагин WordPress с папкой «blocks», которая содержит PHP, базовый CSS и JavaScript, необходимые для построения пользовательского блока. Единственный недостаток, который я заметил, в том, что JavaScript использует старый синтаксис ES5, а не современный ESNext. Современный JavaScript позволяет писать более лаконичный код, а также использовать JSX в коде нашего кастомного блока.
Вы также можете использовать инструмент ‘create-guten-block’ от Ahmad Awais. Он даст вам много стандартных инструментов «из коробки». Среди них будет Webpack, поддержка ESNext и др. Его настройка довольно проста и похожа на создание приложения в React.
Однако вместо использования стороннего инструмента, я использовал один из примеров пользовательских блоков, которые доступны на Github в репозитории gutenberg-examples. Карта примеров демонстрирует многое из того, что вы захотите видеть в минимально интерактивном пользовательском блоке. Она также включает в себя @wordpress/scripts
, пакет для запуска и построения кода JavaScript, благодаря которому можно использовать синтаксис «ESNext» и JSX.
Чтобы начать работу, я просто сделал локальную копию и изменил поля в файле package.json
и запустил yarn
.
Блоки
Что же такое «Блок»? Мне было трудно понять эту концепцию, когда я впервые начал работать с редактором блоков.
Из документации:
Новый блочный редактор разработан для создания многофункциональных гибких макетов под веб-сайты и цифровые продукты. Его блочная система позволяет создавать и форматировать контент. Контент создается в виде блоков вместо текста произвольной формы с добавлением медиафайлов, встроенных объектов и шорткодами…
По сути, «блок» — это организационная единица для редактируемого «барахла» в WordPress. По крайней мере, таково мое понимание.
Блоки — это главный «строительный материал», но как нам его создать? Этот момент я могу объяснить! Блоки практически полностью сделаны в JavaScript. Gutenberg предлагает несколько новых действий (enqueue_block_assets
и enqueue_block_editor_assets
), чтобы вы могли использовать свои JavaScript и таблицы стилей в сочетании с ним.
function fancy_custom_block_block_init() {
// automatically load dependencies and version
$asset_file = include( plugin_dir_path( __FILE__ ) . 'build/index.asset.php');
wp_register_script(
'fancy-custom-block-block-editor',
plugins_url( 'build/index.js', __FILE__ ),
$asset_file['dependencies'],
$asset_file['version']
);
wp_register_style(
'fancy-custom-block-block-editor',
plugins_url( 'editor.css', __FILE__ ),
array( ),
filemtime( plugin_dir_path( __FILE__ ) . 'editor.css' )
);
wp_register_style(
'fancy-custom-block-block',
plugins_url( 'style.css', __FILE__ ),
array( ),
filemtime( plugin_dir_path( __FILE__ ) . 'style.css' )
);
register_block_type( 'fancy-block-plugin/fancy-custom-block', array(
'editor_script' => 'fancy-custom-block-block-editor',
'editor_style' => 'fancy-custom-block-block-editor',
'style' => 'fancy-custom-block-block',
) );
}
add_action( 'init', 'fancy_custom_block_block_init' );
Все, что нужно сделать на PHP — это поставить в очередь ваши JavaScript и CSS файлы и вызвать register_block_type()
с дескриптором каждого актива.
Мир JavaScript
Если вы вдруг думали, что сейчас будет больше PHP, подумайте еще раз! С этого момента мы в мире JavaScript.
Ключевым аспектом создания блоков в Gutenberg является функция registerBlockType()
. Она делает всю работу.
registerBlockType( 'my-block/cool-block-name', {
// ... Massive JS object
}
И это все! До встречи!
…
Ну ладно, есть еще кое-что. Но в целом, речь о создани блока Gutenberg в WordPress на этом заканчивается. Зато в «Massive JS object» есть несколько вещей, на которые стоит обратить внимание.
Если заглянете в файл /05-recipe-card-esnext/src/index.js
из репозитория примеров gutenberg, вы увидите множество параметров, которые используются при создании блока. Мы рассмотрим три основные секции: attributes
и методы edit()
и save()
.
Атрибуты
Если вам нужен блок, который действительно что-то делает, например, позволяет редактировать текст, вы должны использовать систему Gutenberg для управления состоянием. Вот этим и занимается объект attributes
. Он в значительной степени идентичен концепции управления состоянием в React — это объект верхнего уровня, который отслеживает свойства и изменения.
attributes: {
title: {
type: "array",
source: "children",
selector: ".callout-title"
},
mediaID: {
type: "number"
},
mediaURL: {
type: "string",
source: "attribute",
selector: "img",
attribute: "src"
},
body: {
type: "array",
source: "children",
selector: ".callout-body"
},
alignment: {
type: "string"
}
},
Объект JavaScript, представленный выше, используется для настройки внешнего вида блока.
Вы можете заметить, что для каждой редактируемой «штуки» в вашем блоке нужно определить атрибут. Для изображений есть mediaID
и mediaURL
: значения заголовка и содержимого тела, а также общее выравнивание всего. Метод edit()
берет эти атрибуты в качестве аргумента, чтобы мы могли менять их через интерфейс редактора.
edit()
Функция edit()
позволяет вам кастомизировать интерфейс редактирования в Gutenberg. Если вы знакомы с функцией React render()
, то они очень похожи. По сути, вы указываете возвратное выражение, в котором есть ваш JSX.
edit: props => {
const {
className,
isSelected,
attributes: { mediaID, mediaURL, body, alignment, title },
setAttributes
} = props;
useEffect(() => {
// console.log(props);
});
const onChangeTitle = value => {
setAttributes({ title: value });
};
const onSelectImage = media => {
setAttributes({
mediaURL: media.url,
mediaID: media.id
});
};
const onChangeBody = value => {
setAttributes({ body: value });
};
const [imageClasses, textClasses, wrapClass] = sortOutCSSClasses(
alignment || 'left',
className
);
return (
<>
{isSelected && (
<BlockControls key="controls">
<AlignmentToolbar
value={alignment}
onChange={nextAlign => {
setAttributes({ alignment: nextAlign });
}}
/>
</BlockControls>
)}
<div className={wrapClass} key="container">
<div className={imageClasses}>
<div className="callout-image">
<MediaUpload
onSelect={onSelectImage}
type="image"
value={mediaID}
render={({ open }) => (
<Button
className={mediaID ? "image-button" : "button button-large"}
onClick={open}
>
{!mediaID ? __("Upload Image") : <img src={mediaURL} />}
</Button>
)}
/>
</div>
</div>
<div className={textClasses}>
<RichText
tagName="h2"
className="callout-title"
placeholder={__("Write a callout title…")}
value={title}
onChange={onChangeTitle}
/>
<RichText
tagName="div"
multiline="p"
className="callout-body"
placeholder={__("Write the callout body")}
value={body}
onChange={onChangeBody}
/>
</div>
</div>
</>
);
},
Первое, что мы делаем — присваиваем наши атрибуты неким локальным переменным, которые будут использоваться функцией.
const {
className,
isSelected,
attributes: { mediaID, mediaURL, body, alignment, title },
setAttributes
} = props;
Приведенный выше синтаксис — это деструктуризация объекта, и он, в основном, используется для получения эффекта «чем меньше печатать, тем лучше». Следующие несколько функций предназначены для обработки в редакторе событий onChange()
. В основном, когда вы меняете какое-то значение, вы хотите обновить состояние приложения. Вот то, что делает метод setAttributes()
.
const onSelectImage = media => {
setAttributes( {
mediaURL: media.url,
mediaID: media.id,
} );
};
Большая часть того, что включено в метод edit()
— это обработчики событий. Они устанавливают состояние в объекте атрибутов блока. Вы также заметите использование хука useEffect()
. Основной метод хуков в React таков, что если залезть под капот к Gutenberg, то он, по большому счету, просто обертка для React.
Последняя часть функции edit()
— это возвратный оператор. Здесь у нас есть куча JSX-кода, который определяет, как на самом деле выглядит интерфейс Gutenberg. Вы можете загрузить некоторые стандартные компоненты и блоки из пакета @wordpress/block-editor
.
import {
RichText,
MediaUpload,
BlockControls,
AlignmentToolbar
} from "@wordpress/block-editor";
Затем в возвратном операторе вы можете использовать их и присваивать значения атрибутов:
return (
...
<div className={textClasses}>
<RichText
tagName="h2"
className="callout-title"
placeholder={__("Write a callout title…")}
value={title}
onChange={onChangeTitle}
/>
<RichText
tagName="div"
multiline="p"
className="callout-body"
placeholder={__("Write the callout body")}
value={body}
onChange={onChangeBody}
/>
</div>
...
);
В блочном редакторе WordPress есть блок RichText
. Это основной блок, который вы захотите использовать для создания редактируемых областей. Имеются параметры для установки типа HTML тега, который он выводит и много других опций. Атрибут onChange
— это место, где вы описываете функцию onChange()
. Это важно, поскольку подразумевает способ, которым вы обновляете атрибуты блока и всего остального в реальном времени.
save()
Окей, это круто и все такое, но мы пока ничего не сохранили. И следующая часть нашего пользовательского блока — метод save()
. Он определяет как ваш блок будет отображаться во фронт энде.
Есть что-то уникальное в том, как Gutenberg сохраняет данные. Пока вы не сохраните данные в post_meta
, конфигурация блока сериализуется в HTML комментарии над самим блоком. WordPress, вероятно, фильтрует его отображение во фронтэнде, но он виден в записи базы данных.
<!-- wp:fancy-block-plugin/fancy-custom-block {"mediaID":4035} -->
<div class="wp-block-fancy-block-plugin-fancy-custom-block bootstrap-block"><div class="wrap-left-"><div class="image-left-"><img class="the-image" src="http://wpdevelop.devtest/content/uploads/2020/01/image-5.jpg"/></div><div class="text-left-"><h2 class="callout-title">Test 2</h2><div class="callout-body"><p>Test</p></div></div></div></div>
<!-- /wp:fancy-block-plugin/fancy-custom-block -->
Это немного странно, однако обеспечивает обратную совместимость. Если вы когда-нибудь отключите Gutenberg, контент не изменится.
save: props => {
const {
className,
attributes: { title, mediaURL, body, alignment }
} = props;
const [imageClasses, textClasses, wrapClass] = sortOutCSSClasses(
alignment || "left",
className
);
return (
<div className="bootstrap-block">
<div className={wrapClass}>
<div className={imageClasses}>
{mediaURL && <img className="the-image" src={mediaURL} />}
</div>
</div>
<div className={textClasses}>
<RichText.Content
tagName="h2"
className="callout-title"
value={title}
/>
<RichText.Content
tagName="div"
className="callout-body"
value={body}
/>
</div>
</div>
);
}
Метод save()
на самом деле гораздо проще, чем edit()
, при этом вы также получаете значения переданных атрибутов. Больше всего это имеет значение для создания вашей фронт энд разметки и вставки значений. Проще простого.
Проблемы, с которыми я столкнулся
Ладно, мы разобрались с тем, как создается пользовательский блок, но что насчет проблем? Оказывается, есть несколько 😢.
Документацию немного сложно изучать. Она не супер легка для понимания и структурирована как-то странно. Было трудно отслеживать информацию, касающуюся каждого отдельного момента в создании пользовательского блока и, в конечном счете, просто читать примеры кода блока. Придется много ковыряться в коде, чтобы разобраться, как все работает.
Кроме этого, я обнаружил, что очень раздражает работать над блоком, код которого активно меняется. Каждый раз, когда вы перезагружаете Gutenberg, вылезает сообщение «Этот блок, по-видимому, был изменен извне…», поскольку разметка блока изменилась.
Я понимаю, почему вылезает ошибка, но это тормозит работу.
У меня также были странные консольные ошибки из-за включенного SCRIPT_DEBUG
. Эта постоянная нужна для локального использования некомпилированной версии React. Очевидно, это известная проблема, но немного странно видеть предупреждения в консоли из-за включенной константы ядра WordPress.

Далее, инструменты разработчика в React по сути бесполезны, так как имена компонентов React в Gutenberg состоят из одного символа…

Все это говорит о том, что еще есть острые углы.
Альтернативные варианты
Итак, чтобы создать действительно нестандартный блок, надо немного поработать. Но что, если вы хотите построить блочный тип используя UI? Оказывается, это можно сделать с помощью ACF. ACF блоки позволяют делать большую часть работы в привычном интерфейсе ACF полей. Другое преимущество в том, что вы можете написать код для ACF блоков полностью на PHP.
Блоки ACF немного отличаются от пользовательских. Рекомендуемый принцип работы с ними — редактировать поля в сайдбаре редактора и смотреть превью в его основной области. Это немного иной опыт создания блоков, но он больше соответствует традиционной работе с ACF — контент добавляется в поля, за пределами основного редактора.
Если вам надо отображать только значения полей внутри блока, тогда этот вариант может вам подойти. Еще вы можете стилизовать блок ACF с помощью таблицы стилей. Он будет выглядеть также, как на фронтэнде.
Блоки ACF больше напоминают «взлом» Gutenberg, чем официальный способ создания блоков.
Есть и другие плагины для WordPress, которые позволяют вам «собирать» блоки, например, Block Lab и Lazy Blocks. Я их еще не пробовал, но если вы ищете метод создания кастомного блока с минимумом кода — это подходящий вариант.
После того, как все сказано и сделано
Итак, у вас есть инструкция по созданию «пользовательских» блоков для WordPress (или Gutenberg).
Вы уже создавали пользовательский блок Gutenberg? Какое у вас сложилось впечатление? Дайте нам знать об этом в комментариях.
Перевод
Автор Peter Tasker
Источник: https://deliciousbrains.com/custom-gutenberg-block/