Блоки WordPress и редактор Gutenberg

Как создавать свои блоки контента в WordPress? С помощью редактора Gutenberg

Содержание

    В блочном редакторе 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/

    Продукты

    WordPress

    WP это — платформа для управления сайтами №1 в мире. Отличается большим количеством готовых плагинов и тем. Быстрым созданием сайтов.

    Gutenberg

    Gutenberg — это новый блочный редактор WordPress




    Добавить комментарий