Как стать автором
Обновить

WordPress Interactivity API: Подробное объяснение

Уровень сложностиСредний
Время на прочтение32 мин
Количество просмотров2.8K
Автор оригинала: WPLake Learning Hub

WordPress Interactivity - это относительно новый API, который позволяет создавать декларативный фронтенд в WordPress нативно. Декларативный? Да, да, он использует тот же принцип, что React и Vue. Только тут под капотом Preact и собственные директивы. И конечно куда же без SSR, который здесь идет из коробки. Звучит интересно? Давайте разбираться вместе.

Содержание

  1. Когда появился и зачем нужен WP Interactivity API

  2. Определение WordPress Interactivity API

  3. Ключевые аспекты WP Interactivity API

  4. SSR в Interactivity API (рендеринг на стороне сервера)

  5. Где можно использовать WordPress Interactivity API

  6. Пример интерактивного блока

  7. Продвинутые советы касательно Interactivity API

  8. Заключение

1. Когда появился и зачем нужен WP Interactivity API

С выпуском WordPress 6.5 в апреле 2024 года на сцене появилось WP Interactivity API - решение, призванное изменить подход к созданию интерактивных веб-сайтов на базе этой популярной платформы. В последние годы, концепция decoupled (или headless) WordPress становится все популярнее, позволяя разработчикам использовать современные фронтенд-фреймворки, такие как React или Vue.

Однако, использование этого подхода требует значительных временных затрат и имеет свои недостатки. В частности, вы полностью теряете доступ ко всем плагинам и встроенным фичам от WordPress на фронтенде. Например, приходится отказаться от удобных решений, таких как Yoast, и реализовать их функционал вручную.

С появлением WordPress 6.5 сценарий меняется. Новая версия платформы внедряет инструменты реактивности прямо в свое ядро, что позволяет нам использовать современные подходы в создании фронтенда, не прибегая к decoupled/headless подходу. Эта функция разрабатывалась командой WP Core в течение длительного времени и была представлена широкой публике только в апреле 2024 года. Таким образом многие разработчики только начинают знакомится с ее возможностями.

Несмотря на наличие отдельного раздела в официальной документации, посвященного основам WP Interactivity API, он выглядит недостаточно исчерпывающим для полного понимания. Кроме того, некоторые части API упоминаются исключительно в контексте Gutenberg блоков (хотя они могут использоваться независимо), что может создавать путаницу, особенно для разработчиков, незнакомых с процессом создания Gutenberg блоков.

Именно поэтому мы решили подготовить этот обширный и детальный обзор, который позволит вам освоить все аспекты WP Interactivity API и полностью разобраться в его возможностях.

2. Определение WordPress Interactivity API

Теперь давайте разберемся, что именно стоит за названием WordPress Interactivity API.

Interactivity API - это стандартная система директив, основанная на декларативном коде, для добавления интерактивности на фронтенде к блокам.

Другими словами, это способ создания фронтенда WP веб-сайта с использованием декларативного подхода. Если официальное определение кажется вам непонятным, не волнуйтесь, мы разберем его по частям.

Декларативный vs Императивный подход

Декларативный, хмм, что это такое? Если вы не знакомы с React или Vue, это слово может быть для вас совершенно незнакомым. Но так как Interactivity API в WordPress основано на этом принципе, нам придётся это понять. Существуют два разных подхода к созданию фронтенда: императивный и декларативный.

С самых ранних дней веба и на протяжении десятилетий все мы использовали императивный подход. Императивный способ заключается в том, что у нас есть статическая начальная разметка, в которую мы добавляем интерактивность, вручную внося изменения в разметку с помощью JS-кода. Таким образом, мы вручную запрашиваем нужные элементы и делаем изменения для каждого действия отдельно.

Декларативный подход - это современная альтернатива, которая предполагает создание разметки, которая одновременно создает элементы и устанавливает правила их поведения. Когда пользователь взаимодействует с ним, разметка автоматически обновляется в соответствии с правилами поведения, которые мы указали, поэтому нам не нужно вручную запрашивать элементы и делать изменения.

Недостатки императивного подхода

В качестве примера давайте рассмотрим продвинутый аккордеон, который отображает свое состояние над своим содержанием. Хотя вы редко встретите подобные аккордеоны в реальной жизни, идея заключается в том, чтобы продемонстрировать слабые стороны императивного подхода, когда речь идет о работе с несколькими состояниями.

Помимо классической функции сворачивания/разворачивания элементов, нам просто нужно отобразить текущее состояние аккордеона (открыто или закрыто) с именем элемента (таким образом, имя открытого элемента или имя последнего закрытого элемента). Разметка пусть быть такой:

<div class="accordion">
  <div class='accordion__panel'>
    <div class='accordion__heading-opened' hidden>
      Current open item is <span class='accordion__open-item-name'></span>
    </div>
    <div class='accordion__heading-closed'>
      Items are closed.
      <p class='accordion__closed-item' hidden>
        Last opened item is <span class='accordion__closed-item-name'></span>
      </p>
    </div>
  </div>

  <div class='accordion__item'>
    <p class='accordion__item-title'>Title</p>
    <div class='accordion__item-content'>Content</div>
  </div>

  <!--other items-->
</div>

Для скрытия элементов мы будем использовать HTML аттрибут "hidden", а для начального состояния мы скроем элементы '__heading-open' и '__closed-item'. Так как бы выглядела классическая реализация на JS? Вот что-то вроде этого:

document.addEventListener('DOMContentLoaded', () => {
    document.body.querySelectorAll('.accordion__item-title').forEach((title) => {
        title.addEventListener('click', () => {
            let item = title.closest('.accordion__item');
            let isToOpen = !item.classList.contains('accordion__item--open');
            let accordion = item.closest('.accordion');
            let prevItem = accordion.querySelector('.accordion__item--open');

            // Handle closing the previous item
            if (prevItem) {
                prevItem.classList.remove('accordion__item--open');
                accordion.querySelector('.accordion__closed-item').removeAttribute('hidden');
                accordion.querySelector('.accordion__closed-item-name').innerText = prevItem.querySelector('.accordion__item-title').innerText;
            }

            // Toggle the current item
            if (isToOpen) {
                accordion.querySelector('.accordion__heading-closed').setAttribute('hidden', true);
                accordion.querySelector('.accordion__heading-opened').removeAttribute('hidden');
                item.classList.add('accordion__item--open');
                accordion.querySelector('.accordion__open-item-name').innerText = title.innerText;
            } else {
                accordion.querySelector('.accordion__heading-opened').setAttribute('hidden', true);
                accordion.querySelector('.accordion__heading-closed').removeAttribute('hidden');
                item.classList.remove('accordion__item--open');
                accordion.querySelector('.accordion__closed-item-name').innerText = title.innerText;
            }
        });
    });
});

Как вы видите, задача, которая казалась простой и выглядела крайне просто, когда описывалась в тексте, превратилась в серию условных проверок и запросов.

Если вы опытный разработчик, то знаете, как выглядит настоящий JS-код для действительно сложных сценариев в жизни. С императивным подходом мы должны писать цепочку запросов на обновление для каждого возможного действия вручную.

Чем больше действий мы поддерживаем, тем сложнее становится код. Приобретая опыт, мы, конечно, находим более короткие и лучшие решения, но мы не можете написать меньше, чем необходимо минимум, который довольно объёмный .

Преимущества декларативного подхода

Как мы видели выше, основным недостатком императивного подхода является необходимость вручную обрабатывать условия и запросы. Декларативный подход предлагает решение, объявляя (декларируя) элементы и правила их поведения одновременно.

Давайте рассмотрим это на примере продвинутого аккордеона, не вдаваясь пока в детали реализации. Начнем с верхней панели, у нас есть панель с двумя разными заголовками: "__heading-open" и "__heading-closed".

В конкретный момент времени нам нужно отображать только один из них. Итак, мы приходим к выводу, что нам нужно состояние isOpen и мы связываем это состояние с видимостью элементов. Когда isOpen равно true, "__heading-open" виден, а "__heading-closed" скрыт, и наоборот.

Введение этого состояния и перемещение условных проверок в разметку позволило бы нам упростить JS-код и менять интерфейс так же легко, как изменение булевого значения нашего состояния. Давайте посмотрим, как это должно выглядеть в псевдокоде:

<div class='accordion__heading-opened' {if !isOpen then add 'hidden' attribute}>
  Current open item is <span class='accordion__open-item-name'></span>
</div>
<div class='accordion__heading-closed' {if isOpen then add 'hidden' attribute}>
  Items are closed.
  <p class='accordion__closed-item' hidden>
    Last opened item is <span class='accordion__closed-item-name'></span>
  </p>
</div>

Тогда в JS мы можем просто сделать:

isOpen = true || isOpen = false;

и макет изменит свое состояние без цепочки запросов и обновления элементов.

Мы надеемся, что теперь вы понимаете суть декларативного подхода. Не беспокойтесь о деталях реализации или о других элементах аккордеона, так как приведенный выше пример служит только для демонстрации идеи. Ниже, в статье, мы полностью реализуем пример продвинутого аккордеона с использованием WP Interactivity API.

На данный момент вам стоит знать, что в настоящее время существует ряд JS-фреймворков, основанных на декларативном подходе, включая React, Vue, Preact и другие. WordPress Interactivity API - это еще один способ, использующий Preact в качестве основы.

Интерактивность или Реактивность?

Если вы знакомы с React или Vue, то знаете, что оба фреймворка используют термин реактивности.

Например, в документации Vue говорится: "Одной из наиболее отличительных особенностей Vue является ненавязчивая система реактивности. Состояние компонента состоит из реактивных объектов JavaScript. Когда вы их изменяете, обновляется представление.

Этот термин широко используется и описывает ключевую характеристику декларативного подхода, позволяющую макету изменяться сразу же, как только изменяются переменные, используемые в нем.

Что касается интерактивности? Давайте еще раз рассмотрим определение:

Interactivity API - это стандартная система директив, основанная на декларативном коде, для добавления интерактивности на фронтенде к блокам.

Исходя из этого описания, вы можете подумать, что реактивность и интерактивность очень похожи, или даже одно и то же.

На самом деле, Интерактивность (Interactivity) - это просто название, которое WordPress выбрал для этого API. Фронтэнд WordPress Interactivity API основан на Preact, который является реактивным фреймворком. Так что, когда используется WP Interactivity API, можно сказать, что используется реактивный инструмент.

Мы не можем сказать точно, почему было выбрано это имя, но, вероятно, было несколько причин. В общем, слово "интерактивность" гораздо более понятно и дружелюбно, чем реактивность, особенно для тех, кто не знаком с концепцией реактивности.

Кроме того, имейте в виду, что реактивность - это функция, даже если это ключевая функция Interactivity API. Таким образом, API включает в себя и другие вещи, к примеру SSR (Server Side Rendering, рендеринг на стороне сервере), у которого есть своя реализация.

Мы подробно рассмотрим SSR позже, но пока вы должны знать, что Interactivity API охватывает все связанные с ним функции, в то время как реактивность является важной ее частью.

3. Ключевые аспекты WP Interactivity API

WP Interactivity API состоит из двух основных частей: директив (directives) и хранилища (store), которые используются в блоках. Хранилище - это общее понятие, описывающее хранилища состояний (state) и контекста (context).

Давайте разберем новые термины:

Блок (block) - это независимый элемент страницы с собственным хранилищем и шаблоном, который содержит директивы. Один блок может включать другие блоки и также может "общаться" с другими блоками.

Хранилище (store) - это набор переменных, на основе которых мы пишем директивы и добавляем любую логику. Это способ "передать" некоторую переменную в декларативный шаблон.

Директива (directive) - это правило объявления, добавленное в разметку, которое контролирует поведение элемента на основе состояния элемента. В псевдокоде это выглядит так: {if isOpen then add 'hidden' attribute}.

Хотя состояние и контекст имеют различия, которые мы рассмотрим ниже, оба они действуют как набор переменных в пределах конкретных элементов.

Если вы знакомы с React или Vue, вы, вероятно, легче поймете данные концепции. Однако у WP Interactivity API собственная реализация, и вы не можете напрямую использовать приемы из мира React.

Если вы сталкиваетесь со всем этим впервые, не волнуйтесь, если не все термины вам понятны. В этой главе мы подробно рассмотрим каждый из них и применим их к упомянутому выше примеру аккордеона.

Примечание к использованию

Имейте в виду, что хотя WP Interactivity API встроен в ядро WP, он не применяется ко всему HTML по умолчанию. По умолчанию он работает только в кастомных блоках Gutenberg, для которых включена соответствующая опция. API также поддерживается Advanced Views фреймворком.

Мы рекомендуем прочитать эту главу без практического воспроизведения. После ознакомления с основными концепциями вы можете попробовать их реализовать самостоятельно. В главе "Где можно использовать API" ниже мы поделимся, когда и как вы можете этим воспользоваться.

3.1) Блок (block)

Давайте начнем с базового понятия.

Блок (block) - это независимый элемент страницы с собственным хранилищем и шаблоном, который содержит директивы. Один блок может включать другие блоки и также может "общаться" c другими блоками.

Мы можем превратить любой HTML-тег в блок так же просто, как добавить data атрибут. Таким образом, в нашем случае первый элемент аккордеона будет блоком.

<div class="accordion" data-wp-interactive="my-accordion">
  <!-- внутренние элементы HTML здесь -->
</div>

Атрибут data-wp-interactive необходим для 'маркировки' конкретного элемента как блока. Все внутри него будет рассматриваться как части блока. Как упоминалось выше, у нас может быть блок внутри блока, так что:

<div class="accordion" data-wp-interactive="my-accordion">
  <!-- внутренние элементы HTML здесь -->
  <div class='popup' data-wp-interactive="my-popup"></div>
</div>

Абсолютно рабочий пример. В качестве значения атрибута мы можем передавать любую строку, но она должна быть уникальной в пределах страницы. Мы рекомендуем всегда давать понятные и осмысленные имена, потому что для "общения" с каким-либо блоком на странице из другого блока мы будем использовать именно то имя, которое определено в этом атрибуте.

3.2) Состояние (state)

Итак, мы определили что такое интерактивный блок. Но прежде чем мы используем какие-либо 'волшебные' директивы, нам нужно определить некоторые данные, которые можно использовать в директивах.

Состояние (state) - это набор переменных, на основе которых мы можем писать директивы и добавлять любую логику. Это один из способов "передать" некоторую переменную в декларативный шаблон. Основные характеристики состояния блока состоят в том, что оно глобальное и публичное, т.е. сохраняется в области видимости страницы под именем блока и доступно другим.

Примечание: Состояние - это опциональная функция, поэтому мы можем создать интерактивный блок без состояния.

Таким образом, любые переменные, определенные в состоянии, являются:

  • Глобальными для всех блоков одного типа (в пределах текущей страницы).
    Это означает, что даже если у вас есть несколько блоков одного типа на одной странице, они все будут использовать одно и то же состояние. Это основное отличие от контекста, который позволяет определить переменные только для 'текущего блока'.

  • Публичными.
    Это означает, что другие блоки могут 'запросить' их значения по имени переменной и использовать имя блока в качестве 'пространства имен'.

Переменная состояния может быть передана с бэкэнда или определена на фронтэнде. Поскольку WordPress - это PHP-фреймворк, состояние может быть передано с бэкэнда путем вызова PHP-функции.

Если нам нужно определить переменную состояния на бэкэнде, мы должны вызвать определенную функцию выше определения блока. Функция называется wp_interactivity_state. Давайте введем состояние isOpen и добавим его в наш аккордеон.

<?php echo wp_interactivity_state( 'my-accordion', [
 'isOpen' => false,
] ); ?>
<div class="accordion" data-wp-interactive="my-accordion">
  <!-- inner HTML elements here -->
</div>

Функция wp_interactivity_state - это функция WordPress, которая принимает два аргумента: имя блока и массив переменных состояния.

Если у нас нет ничего, что нужно передать с бэкэнда, мы можем определить состояние на фронтэнде в JS-коде таким образом:

const { state } = store("my-accordion", {
  state: {
    isOpen: false
  },
});

Пока проигнорируйте, откуда берется функция store и куда ее помещать. На данный момент вам просто нужно понять, что состояние - это способ "передать" переменные для директив, и его можно определить как на бэкэнде, так и на фронтэнде.

Заметка: Также поддерживается "смешанный" способ, поэтому вы можете "передавать" некоторые переменные состояния с бэкэнда, в то время как другие определять на фронтэнде.

3.3) Контекст (context)

Контекст - это еще один способ определения переменных, которые могут быть использованы в директивах.

Контекст (context) - это набор переменных, на основе которых мы можем писать директивы и добавлять любую логику. Это один из способов "передать" некоторую переменную в декларативный шаблон. Основные характеристики контекста блока состоят в том, что он локальный и приватный, т.е. сохраняется в пределах текущего блока и недоступен другим.

Примечание: Контекст - это опциональная функция, поэтому у нас может быть интерактивный блок без контекста.

Таким образом, любые переменные, определенные в контексте, являются:

  • Доступными только для текущего блока.
    Это означает, что даже если у вас есть несколько блоков одного типа на одной странице, у каждого из них будет свой собственный контекст. Это основное отличие от состояния, которое позволяет обмениваться переменными между экземплярами блока одного типа.

  • Приватными.
    Это означает, что другие блоки не могут напрямую 'запросить' их значения.

  • Наследуемыми.
    Переменные контекста доступны текущему элементу и всем его внутренним элементам.

В отличие от переменных состояния, которые могут быть определены как на бэкэнде, так и на фронтэнде, контекст может быть определен только на бэкэнде. Переменные контекста должны передаваться в формате JSON с использованием атрибута data-wp-context. Давайте добавим переменную isOpen в контекст нашего аккордеона:

<div class="accordion" data-wp-interactive="my-accordion" 
data-wp-context='{"isOpen": false}'>
 <!-- inner HTML elements here -->
</div>

Так это выглядит в разметке. Но на практике вы, вероятно, будете передавать PHP-переменные. Это можно сделать так:

<div class="accordion" data-wp-interactive="my-accordion" 
data-wp-context='<?php echo json_encode(["isOpen" => false]); ?>'>
 <!-- inner HTML elements here -->
</div>

Вы можете определить атрибут data-wp-context вручную или вызвать специальную WP-функцию wp_interactivity_data_wp_context, как показано ниже:

<div class="accordion" 
data-wp-interactive="my-accordion" 
<?php echo wp_interactivity_data_wp_context(["isOpen" => false]); ?>'>
 <!-- inner HTML elements here -->
</div>

Как мы уже упоминали, контекст является приватным и наследуемым, поэтому он доступен только для текущего узла и всех его дочерних элементов. Таким образом, в блоке может быть несколько контекстов:

<div class="accordion" data-wp-interactive="my-accordion">
  <div data-wp-context='<?php echo json_encode(["someVar" => "some value"]); ?>'>
     <!-- inner HTML elements here -->
  </div>
  <div data-wp-context='<?php echo json_encode(["anotherVar" => true]); ?>'>
     <!-- inner HTML elements here -->
  </div>
</div>

3.4) Директивы (directives)

Теперь давайте рассмотрим директивы, ключевую функцию Interactivity API, которая позволяет нам создавать реактивные макеты.

Директивы (directives) - это пользовательские атрибуты, добавленные в разметку вашего блока для определения поведения HTML элементов.

Другими словами, директивы позволяют управлять поведением разметки на основе состояния или контекста блока.

На первый взгляд они кажутся обычными HTML-атрибутами данных, знакомыми каждому. Они следуют определенному формату, записываясь как data-wp-{x}="y", где x - это имя директивы, а y - значение.

Для иллюстрации, в нашим примере аккордеона давайте вспомним часть заголовка в псевдокоде:

<div class='accordion__heading-opened' {if !isOpen then add 'hidden' attribute}>
  Current open item is <span class='accordion__open-item-name'></span>
</div>
<div class='accordion__heading-closed' {if isOpen then add 'hidden' attribute}>
  Items are closed.
  <p class='accordion__closed-item' hidden>
    Last opened item is <span class='accordion__closed-item-name'></span>
  </p>
</div>

Теперь давайте перепишем это в реальный код, использующий WP Interactivity API:

<div class="accordion"
     data-wp-interactive="my-accordion"
     data-wp-context='{"isOpen": false}'>
    <div class='accordion__panel'>
        <div class='accordion__heading-opened' data-wp-bind--hidden="!context.isOpen">
            Current open item is <span class='accordion__open-item-name'></span>
        </div>
        <div class='accordion__heading-closed' data-wp-bind--hidden="context.isOpen">
            Items are closed.
        </div>
    </div>
     <!-- other elements -->
</div>

В этом примере:

  • Мы определили блок 'my-accordion'.

  • Определили переменную isOpen в контексте блока.

  • Добавили директивы 'data-wp-bind--hidden' к целевым элементам.

Объяснение директив

Давайте рассмотрим первую директиву: data-wp-bind--hidden="!context.isOpen".

Для имени атрибута данных мы использовали:

  • data-wp- как общий префикс, необходимый для любой директивы.

  • bind, который является именем директивы.
    Bind - это одна из директив Interactivity API, которая позволяет управлять атрибутами на основе булевых переменных.

  • --hidden, представляющая имя атрибута, который мы хотим контролировать.
    Здесь мы можем поместить любой допустимый HTML-атрибут.

Теперь давайте рассмотрим значение: !context.isOpen.

В значении директивы мы можем использовать одну из следующих зарезервированных переменных: state, context, actions, callbacks. На данный момент давайте сосредоточимся на контексте.

Эта переменная контекста дает нам доступ к текущему контексту элемента. Наша переменная isOpen является булевой, поэтому она подходит для директивы bind, позволяющей динамически управлять атрибутами.

Написав data-wp-bind--hidden="!context.isOpen", мы добавили правило поведения. Оно гласит: добавьте атрибут 'hidden' к этому узлу, если переменная context.isOpen равна false, и удалите этот атрибут, если она изменится на true.

Это реактивный код, создающий привязку, которая сохраняется до закрытия страницы. Даже если вы позже измените isOpen после какого-то действия, или по истечении времени, данная привязка сохранит наше правило и обновит атрибут в соответствии с новым значением переменной.

Почему Interactivity API использует директивы вместо JSX или чего-то еще?

Если вы знакомы с Vue или React, вы можете провести аналогию с их подходами. Например, в Vue мы также используем встроенные директивы, такие как v-bind="srcVariable", а в React мы используем className={className}. В WordPress же мы используем data-wp-class--classname="y".

Хотя длинный формат директив изначально может вас огорчать, вы, вероятно, согласитесь, что их имена довольно понятные. Имейте в виду, что WordPress построен на классической основе, и WP Interactivity API разработан для работы с любым классическим HTML-кодом. Здесь официальная документация объясняет все причины выбора директив.

Список доступных директив

Interactivity API предоставляет ряд директив, которые покрывают все наши потребности. Вы можете найти полный список на этой странице официальной документации. Давайте рассмотрим часто используемые:

  • wp-bind
    Как вы видели, эта директива позволяет управлять любым HTML-атрибутом, например, data-wp-bind--hidden="context.isOpen".

  • wp-text
    Позволяет контролировать innerText элемента. Внутри значения вы должны передать строковую переменную, например, data-wp-text="state.submitLabel".

  • wp-class
    Позволяет управлять наличием класса на основе булевого значения. Например, data-wp-class--active="context.isActive". Имя класса может содержать любые допустимые символы, так что не беспокойтесь, класс вроде button--enabled не сломает директиву.

  • wp-style
    Позволяет управлять встроенными стилями, например, data-wp-style--color="context.color".

  • wp-on
    Позволяет назначать обработчики событий. Например, data-wp-on--click="actions.submit".

3.5) JS код

Теперь давайте рассмотрим последний элемент: сам JavaScript-код, который позволяет нам добавлять обработчики событий и совершать различные действия, такие как отправка Ajax запроса. На фронтенде WordPress Interactivity API доступен в виде отдельного файла - небольшой библиотеки (35KB), основанной на Preact.

Interactivity.js

Мы импортируем эту библиотеку в JavaScript-коде блока. Не все блоки требуют JavaScript-код, поэтому ее использование является необязательным. Если нам не нужно добавлять обработчики к нашему блоку, то мы не добавляем JavaScript-код, и, следовательно, библиотека не будет импортирована.

Как мы уже упоминали ранее, Interactivity API предоставляет рендеринг на стороне сервера (Server-Side Rendering) из коробки, поэтому директивы будут выполняться и на стороне сервера. Когда нам нужно задать переменные состояния на фронтенде или определить какие-либо действия, мы должны добавить следующую строку в наш JS-код:

import { store } from '@wordpress/interactivity';

Это классический импорт JavaScript модуля, а @wordpress/interacivity - это псевдоним для /wp-includes/js/dist/interactivity.min.js, добавленный WordPress с помощью функции ImportMap. Следующие функции доступны для импорта: store, getContext и getElement. Давайте рассмотрим их по очереди.

Функция Store

Начнем с функции store, которая является основной. Итак, чтобы определить блок в JavaScript, мы должны вызвать функцию store, импортированную из библиотеки Interactivity.

Первый аргумент должен быть именем нашего блока из атрибута data-interactive, а второй - объект с настройками. Давайте добавим состояние isClosed к нашему блоку аккордеона:

import { store } from '@wordpress/interactivity';

const { state } = store("my-accordion", {
  state: {
    isClosed: false,
  },
});

Объект поддерживает следующие ключи: state, actions, callbacks. Элементы, определенные внутри ключа state, используются в качестве переменных состояния. Элементы внутри actions используются в директивах действий, таких как wp-on--click. Callbacks предназначены для внутренних событий, таких как init, которое вызывается при парсинге элемента.

Если вы помните, эти ключи также предварительно определены в директивах, поэтому, определяя ключи в этих элементах, мы определяем переменные и можем использовать их также в директивах. В этом случае мы определили переменную состояния isClosed, поэтому в директивах мы можем написать: state.isClosed.

Примечание: Как мы уже упоминали ранее, вы можете определить состояние как на бэкенде, так и на фронтенде. Это означает, что если мы определили состояние isOpen на бэкенде с помощью функции wp_interactivity_state, мы можем получить доступ к этому свойству и в JavaScript-коде, не определяя его снова.

Таким образом, мы можем написать let isOpen = state.isOpen в нашем JS-коде, и он вернет значение переменной состояния, которую мы определили на бэкенде, так как WordPress автоматически передаст их на фронтенд в виде JSON. Однако помните, что переменные состояния, определенные в JavaScript, будут доступны только на стороне клиента.

Это означает, что если вы не определили переменную состояния на бэкенде, а только на фронтенде, вы все равно можете использовать это "только фронтенд-состояние" в директивах. Однако такие директивы будут пропущены во время рендеринга на стороне сервера и выполнены только на фронтенде.

Следовательно, если вы используете их для управления интерфейсом, клиент может увидеть элемент, который вы хотите скрыть, до тех пор, пока JavaScript не будет загружен и выполнен. Поэтому, если вы используете переменные состояния в директивах, которые влияют на начальный интерфейс, мы рекомендуем определять их на бэкенде.

Функция getContext

Кроме состояния, мы также можем получить доступ к контексту блока. Для этого нам нужно импортировать функцию getContext из библиотеки Interactivity. После этого мы можем вызвать getContext() в любом действии и получить любую переменную контекста в качестве свойства.

import { store, getContext } from '@wordpress/interactivity';

store("my-accordion", {
    actions: {
        toggleItem: () => {
            let context = getContext();
            context.isItemClosed = !context.isItemClosed;
        }
    }
});

Аналогично переменным состояния, переменные контекста также могут быть изменены, так что вы можете перезаписывать их по мере необходимости, и это обновит все директивы, где используется измененная переменная контекста.

Примечание: Вызов getContext() автоматически получит ближайший контекст к элементу, на котором сработало событие. Это означает, что если вы добавили data-wp-context к блоку, а также к его дочернему элементу, а затем добавили обработчик клика к этому дочернему элементу, вызов getContext() в методе вернет контекст ближайшего элемента, в данном случае дочернего.

Также обратите внимание, что в отличие от состояния, контекст доступен через функцию, потому что, как вы помните, контекст определяется через атрибут data-wp-context, тогда как состояние - через вызов функции wp_interactivity_state.

Переменные состояния в действии (state in actions)

Давайте объединим состояние и действие, чтобы добавить реакцию на клик в наш пример аккордеона и увидеть, как это все выглядит вместе:

import { store } from '@wordpress/interactivity';

const { state } = store("my-accordion", {
  state: {
    isClosed: true,
  },
  actions: {
    toggle: (event) => {
      state.isClosed = !state.isClosed;
    },
  },
});

Затем мы назначаем указанный обработчит нашему элементу аккордеона, используя директиву wp-on:

<div class='accordion__item'>
  <p class='accordion__item-title' data-wp-on--click="actions.toggle">Title</p>
  <div class='accordion__item-content' data-wp-bind--hidden="!state.isOpen">Content</div>
</div>

Вот и все! Благодаря реактивности, когда мы изменяем переменную состояния, будут выполнены директивы, в которых эта переменная использовалась, и атрибут hidden будет добавлен или удален. Мы можем использовать одну и ту же переменную в нескольких директивах.

Функция getElement

С помощью функции getElement мы можем напрямую получить доступ к HTMLElement текущего блока. В большинстве случаев это не понадобится, но бывают редкие случаи, когда это полезно. Например, если вам нужно получить доступ к API браузера, чтобы прокрутить содержимое внутри элемента или получить ширину элемента.

Чтобы получить HTMLElement, нам нужно использовать ref часть из возврата функции:

const { ref } = getElement();
// ref is an ordinary HTMLElement instance, so we can do anything with it, like:
console.log(ref.getBoundingClientRect());

Обратные вызовы (callbacks)

Возвращаясь к ключам объекта store: state, actions и callbacks - мы уже видели первые два в действиях. Третий ключ, callbacks, используется для добавления обработчиков на внутренние события, которые вызываются самой библиотекой, таких как init или run.

init вызывается только один раз, сразу после парсинга элемента браузером, в то время как run вызывается при каждом рендеринге элемента. Чтобы добавить их в блок, помимо определения их в JS, нам нужно указать их и в директивах, вот так:

import {store, getElement, getContext} from '@wordpress/interactivity';

store("my-accordion", {
    callbacks: {
        init: () => {
            let {ref} = getElement();

            console.log('Accordion node is parsed', {
                HTMLElement: ref,
                isOpen: getContext().isOpen,
            });
        }
    }
});
<div class="accordion"
     data-wp-interactive="my-accordion"
     data-wp-context='{"isOpen": false}'
     data-wp-init="callbacks.init">
    <!--inner items-->
</div>

Таким образом, мы можем определить обработчики, чтобы управлять поведением наших блоков при их инициализации и рендеринге.

3.6) Краткий итог

Самое время сделать глубокий выдох, потому что на этом этапе мы можем вас поздравить - самые сложные части остались позади, и вы уже ознакомились с ключевыми аспектами Interactivity API.

Давайте рассмотрим их все сразу, чтобы у вас появилась полная картина.

WordPress Interactivity API основан на простом HTML и предоставляет директивы, состояние и контекст, которые позволяют создавать декларативные шаблоны. Рассмотрим простой пример:

<?php wp_interactivity_state('my-accordion', [
    'isClosed' => true,
]) ?>
<div class="accordion"
     data-wp-interactive="my-accordion"
     data-wp-context='{"isOpen": false}'
     data-wp-init="callbacks.init">
    <div class='accordion__panel'>
        <div class='accordion__heading-opened' data-wp-bind--hidden="state.isClosed">
            Current open item is <span class='accordion__open-item-name'></span>
        </div>
        <div class='accordion__heading-closed' data-wp-bind--hidden="context.isOpen">
            Items are closed.
        </div>
    </div>
    <!--other items-->
</div>
import { store, getElement, getContext } from '@wordpress/interactivity';

const { state } = store("my-accordion", {
    callbacks: {
        init: () => {
            let { ref } = getElement();

            console.log('Accordion node is parsed', {
                HTMLElement: ref,
                isOpen: getContext().isOpen,
                isClosed: state.isClosed,
            });
        }
    }
});

Что здесь происходит?

1. На PHP бэкенде происходит обработка директив и изменение HTML

Перед передачей в браузер WordPress обрабатывает директивы и обновляет разметку соответствующим образом. Кроме того, WP конвертирует все переменные состояния в JSON и передает их в браузер вместе с разметкой.

В нашем случае элемент __heading-opened будет иметь атрибут hidden в соответствии со значением false переменной контекста isOpen. В то же время элемент __heading-closed не будет иметь этот атрибут, так как переменная состояния isClosed равна true.

На фронтенде наш JS код загружает библиотеку Interactivity API

Эта библиотека парсит JSON (в React подобное называется регидратацией, rehydration) и вызывает наше определение блока. Библиотека WP Interactivity.js поместит переменные состояния, определенные на бэкенде, в переменную состояния в нашем JS коде (в нашем случае isClosed).

Она также вызовет callback init, к которому мы добавили обработчик, используя директиву wp-init. В этом callback мы выводим HTML элемент блока вместе с переменной isOpen из контекста и переменной isClosed из состояния.

С логической точки зрения, нет смысла устанавливать обе переменные одновременно, но мы добавили их, чтобы показать, как можно использовать и состояние, и контекст одновременно. Вот как работает WordPress Interactivity API, а теперь убедитесь, что у вас сложилась полная картина.

Если что-то осталось неясным, мы рекомендуем перечитать соответствующие объяснения выше, прежде чем продолжить чтение статьи.

4. SSR в Interactivity API (рендеринг на стороне сервера)

Примечание: Эта информация полезна для понимания SSR в Interactivity API, но не обязательна для базового использования API. Вы можете пропустить эту главу.

Общая информация про SSR

Главный недостаток любых реактивных фреймворков в JS - это рендеринг на стороне клиента. В то время как классические приложения отправляют готовый HTML клиенту, реактивные фреймворки, такие как React или Vue, создают HTML "на лету" на основе определенных компонентов и их данных.

На практике это означает, что клиенты увидят пустую страницу или, по крайней мере, её части на некоторое время, пока JS обрабатывает всё. Это не только ухудшает UX, но и SEO, так как поисковые системы не могут сразу проанализировать содержание страницы.

Многие поисковые системы не поддерживают подобное, и хотя Google утверждает, что поддерживает, SEO-эксперты не рекомендуют использовать рендеринг на стороне клиента для страниц, чувствительных к SEO-показателям. Если вы знакомы с React/Vue, вы знаете, что фулстек фреймворки, такие как Next.js (React) и Nuxt.js (Vue), предлагают SSR.

Они могут "предзагружать" HTML, выполняя JavaScript на стороне сервера (Node.js) и передавая уже подготовленный HTML вместе с необходимыми состояниями, чтобы клиент мог "гидрировать" (hydration, наполнение водой концентрата) эти данные и восстановить нужное состояние. Эти фрейморки заботаться обо всех нюансах, но за кулисами это требует значительных усилий.

Одна из сложностей здесь в том, что фреймворки используют Node.js на серверной стороне и обычный JavaScript на клиентской стороне в браузере. Это означает, что один и тот же кусок кода может выполняться в любой из этих сред, которые существенно различаются.

SSR в WP Interactivity API

Теперь вернемся к WordPress. Как уже упоминалось, Interactivity.js на стороне клиента основан на Preact, поэтому он не предлагает SSR. К счастью, у WordPress есть свое собственное решение для SSR, и хотя оно может показаться немного грубым, это хорошее решение, поддерживающее любой обычный HTML.

SSR в Interactivity API WordPress основан на собственном HTML API (WP_HTML_Tag_Processor), который WordPress представил в версии 6.2.

Идея заключается в том, чтобы парсить фрагменты HTML с интерактивными директивами на серверной стороне (PHP) и изменять их разметку, чтобы предоставить актуальную разметку клиенту. Кроме того, это включает "гидратацию" на стороне клиента для восстановления всех состояний на клиентской стороне.

Таким образом, все переменные состояния, которые мы добавляем к блокам с помощью функции wp_interactivity_state в PHP, и контекстные переменные из атрибута data-wp-context будут использоваться во время выполнения директив в PHP SSR.

После этого переменные состояния будут добавлены на текущую страницу в виде JSON, а затем будут разобраны на клиенте и привязаны к состояниям JS-блоков, как мы показали в объяснении JS-кода. Таким образом, клиент и поисковые системы получают готовый HTML с самого начала, а разработчики доступ ко всем данным с бэкенда.

Хотя это и потребовало поддержки директив в PHP от WordPress, данное решение отлично интегрируется в экосистему WP, облегчая жизнь разработчиков и улучшая пользовательский опыт. Если вы думаете, что это немного грубое решение, которое может ухудшить производительность, то вам не стоит об этом беспокоиться.

Это реализовано должным образом, так что WordPress не анализирует весь HTML страницы, а только те фрагменты, где ожидается использование Interactivity API, дополнительно ограничивая парсинг только элементами с атрибутами data-wp. Таким образом, это не добавляет значительной нагрузки на процесс в целом.

5. Где можно использовать WordPress Interactivity API

Теперь, когда мы изучили основы, давайте посмотрим, где мы можем применить эти знания. Как уже упоминалось, SSR Interactivity API не применяется ко всему содержимому страницы, поэтому по умолчанию вы не можете начать использовать его в любом шаблоне. Ниже приведены места, в которых вы можете использовать API:

5.1) В кастомных блоках Gutenberg (созданных вручную)

По умолчанию Interactivity API WordPress доступен в блоках Gutenberg. Чтобы включить поддержку API для конкретного блока, необходимо определить "interactivity": true в данных block.json и затем вы можете использовать все возможности Interactivity API в файлах render.php и view.js.

Это хорошая новость в том случае если вы уже знакомы и имеете опыт создания кастомных блоков Gutenberg. В противном случае, мы бы не рекомендовали этот метод, так как создание кастомных блоков с нуля занимает много времени и требует знаний React.

В большинстве случаев вам потребуется писать разметку для одного и того же блока дважды: сначала в React для редактора Gutenberg, затем в PHP для фронтенда.

5.2) В кастомных блоках Gutenberg (созданных с использованием сторонних плагинов)

В целом, создание страниц WordPress из кастомных блоков Gutenberg довольно хорошая идея, поскольку они модульные, эффективны на фронтенде и обеспечивают хороший UX для редакторов. В нашем агентстве мы используем функцию ACF Blocks плагина Advanced Custom Fields.

Эта функция позволяет создавать кастомных блоки Gutenberg без лишних хлопот. Вы также можете использовать функции MB Blocks или Pods Blocks. Ознакомьтесь с нашим обзором лучших плагинов для создания пользовательских полей, чтобы сравнить и узнать, как их использовать.

5.3) В шаблонах Advanced Views Framework

Advanced Views Framework представляет умные шаблоны для фронтенда WordPress, упрощая запросы к постам и создание шаблонов. Эти шаблоны используют движок Twig и поддерживают Interactivity API из коробки, поэтому вы можете использовать API в любом шаблоне без дополнительных действий.

Эти шаблоны могут храниться внутри вашей темы, что делает их удобными для работы с Git и IDE. Кроме того, вы можете использовать TypeScript/Sass и Tailwind для них.

Еще одно преимущество заключается в том, что фреймворк также поддерживает создание блоков Gutenberg (использую упомянутые выше плагины), поэтому вы можете превратить любой шаблон в кастомный блок Gutenberg, обеспечивая и удобство для редакторов, и пользуясь при этом модульным подходом и возможностями Interactivity API.

5.4) В простых PHP-шаблонах

Хотя Interactivity API может показаться инструментом, ориентированным исключительно на Gutenberg, на самом деле это не так. Это общедоступное API, которое можно использовать где угодно.

В вашей теме или плагине вы можете вызвать функцию wp_interactivity_process_directives и передать строку с HTML-кодом, содержащим директивы. Эти директивы будут выполнены, и функция вернет обновленную разметку.

Затем, в вашем JS-коде, вы можете импортировать библиотеку interactivity.js и использовать ее как обычно. Единственное отличие заключается в том, что в этом случае WordPress не добавит псевдоним @wordpress/interactivity, и вам нужно будет использовать полный путь к файлу: /wp-includes/js/dist/interactivity.min.js.

Это может выглядеть так:

<?php

echo wp_interactivity_state('my-accordion', [
    'isOpen' => false,
]);

ob_start();
?>

<div class="accordion" data-wp-interactive="my-accordion">
  <!-- inner HTML elements here -->
</div>

<?php

$html = (string) ob_get_clean();
echo wp_interactivity_process_directives($html);
import { store, getElement, getContext } from '/wp-includes/js/dist/interactivity.min.js';

store('my-accordion', {
    actions: {
        // ...
    }
});

Несмотря на возможность использовать Interactivity API где угодно, мы рекомендуем создавать сайты модульно, используя независимые блоки с собственными стилями и скриптами, как в случае с кастомными блоками Gutenberg или умными шаблонами Advanced Views Framework.

6. Пример интерактивного блока

Теперь когда у вас есть все необходимые знания, мы рекомендуем вам создать несколько примеров своими руками, чтобы попрактиковаться и лучше усвоить полученные знания. Ниже, как и обещали, приведим пример продвинутого аккордеона, упомянутого выше, превращенный в интерактивный блок WordPress.

<?php 

wp_interactivity_state( 'my-accordion', [
	'isOpen'             => false,
	'isLastItemSet'      => false,
	'lastOpenedItemName' => "",
] ); ?>

<div class="accordion"
     data-wp-interactive="my-accordion">
    <div class='accordion__panel'>
        <div class='accordion__heading-opened' data-wp-bind--hidden="!state.isOpen">
            Current open item is <span class='accordion__open-item-name' data-wp-text="state.lastOpenedItemName"></span>
        </div>
        <div class='accordion__heading-closed' data-wp-bind--hidden="state.isOpen">
            Items are closed.
            <p class='accordion__closed-item' data-wp-bind--hidden="!state.isLastItemSet">
                Last opened item is <span class='accordion__closed-item-name'
                                          data-wp-text="state.lastOpenedItemName"></span>
            </p>
        </div>
    </div>

    <div class='accordion__item' data-wp-context='{"isItemClosed":true,"itemName":"First"}'>
        <p class='accordion__item-title' data-wp-on--click="actions.toggleItem" data-wp-text="context.itemName"></p>
        <div class='accordion__item-content' data-wp-bind--hidden="context.isItemClosed">Content of the first item</div>
    </div>

    <div class='accordion__item' data-wp-context='{"isItemClosed":true, "itemName":"Second"}'>
        <p class='accordion__item-title' data-wp-on--click="actions.toggleItem" data-wp-text="context.itemName"></p>
        <div class='accordion__item-content' data-wp-bind--hidden="context.isItemClosed">Content of the second item
        </div>
    </div>
</div> 
import {store, getContext} from '@wordpress/interactivity';

const {state} = store("my-accordion", {
    state: {
        openedItemTitle: null,
        get isLastItemSet() {
            return '' !== state.lastOpenedItemName;
        },
        get isOpen() {
            return null !== state.openedItemTitle;
        }
    },
    actions: {
        toggleItem: (event) => {
            let titleElement = event.target;
            let context = getContext();

            // Handle closing the previous item
            if (null !== state.openedItemTitle &&
                titleElement !== state.openedItemTitle) {
                state.openedItemTitle.click();
            }

            // Toggle the current item
            context.isItemClosed = !context.isItemClosed;

            // update the top state
            state.lastOpenedItemName = context.itemName;
            state.openedItemTitle = false === context.isItemClosed ?
                titleElement :
                null;
        }
    }
});

Getters: В этой реализации мы использовали встроенную функцию getters в JavaScript для добавления динамических переменных состояния. Эта функция может быть добавлена к любому объекту в JS и особенно полезна в нашем случае, поскольку wp-bind директива поддерживает только булевые примитивы, так что мы не могли бы установить там условия.

Итак, это декларативная реализация блока аккордеона. Давайте теперь вспомним, как выглядел JS-код в императивном подходе:

document.addEventListener('DOMContentLoaded', () => {
    document.body.querySelectorAll('.accordion__item-title').forEach((title) => {
        title.addEventListener('click', () => {
            let item = title.closest('.accordion__item');
            let isToOpen = !item.classList.contains('accordion__item--open');
            let accordion = item.closest('.accordion');
            let prevItem = accordion.querySelector('.accordion__item--open');

            // Handle closing the previous item
            if (prevItem) {
                prevItem.classList.remove('accordion__item--open');
                accordion.querySelector('.accordion__closed-item').removeAttribute('hidden');
                accordion.querySelector('.accordion__closed-item-name').innerText = prevItem.querySelector('.accordion__item-title').innerText;
            }

            // Toggle the current item
            if (isToOpen) {
                accordion.querySelector('.accordion__heading-closed').setAttribute('hidden', true);
                accordion.querySelector('.accordion__heading-opened').removeAttribute('hidden');
                item.classList.add('accordion__item--open');
                accordion.querySelector('.accordion__open-item-name').innerText = title.innerText;
            } else {
                accordion.querySelector('.accordion__heading-opened').setAttribute('hidden', true);
                accordion.querySelector('.accordion__heading-closed').removeAttribute('hidden');
                item.classList.remove('accordion__item--open');
                accordion.querySelector('.accordion__closed-item-name').innerText = title.innerText;
            }
        });
    });
});

Вау, насколько короче стал обработчик событий теперь, и как здорово обновлять переменные вместо того, чтобы напрямую запрашивать элементы и вручную обновлять атрибуты!

Очевидно значительное преимущество нового подхода даже в этом небольшом примере. Вспомните весь свой JS-код из реальных проектов и представьте себе, насколько лучше будет, когда он будет написан с использованием Interactivity API.

А сейчас самое время для экспериментов! Попробуйте что-то изменить в этом примере, чтобы получить практический опыт работы с WordPress Interactivity API!

Заметка о демонстрационном коде: Если вы опытный веб-разработчик, то можете заметить, что наши реализации довольно просты и могут быть улучшены. В реальной жизни мы бы использовали директиву wp-each или по крайней мере цикл PHP по массиву данных. Но не судите строго, мы упростили код насколько это возможно в демонстрационных целях.

7. Продвинутые советы касательно Interactivity API

Есть несколько полезных вещей, которые вам стоит знать, чтобы раскрыть полный потенциал API. Некоторые из них мы уже кратко упомянули, и теперь давайте рассмотрим их подробнее:

7.1) Getters в состоянии

Как вы видели в декларативной реализации продвинутого аккордеона выше, мы можем определить любые геттеры в объекте состояния. Это очень полезная функция, и вы будете часто ее использовать из-за того, что большинство директив поддерживают только булевые переменные.

Это означает, что вы не можете написать условные выражения, как в обычном JS-коде, например, data-wp-bind--hidden="state.isFirst && !state.isSecond" или "state.name == ''". Подобное просто не будет работать. Поэтому, когда вам нужны такие условия, вы можете поместить их в геттер и определить этот геттер внутри атрибута.

import {store, getContext} from '@wordpress/interactivity';

const {state} = store("my-accordion", {
    state: {
        get isLastItemSet() {
            return '' !== state.lastOpenedItemName;
        },
    },
});

Внутри этого геттера мы также можем использовать переменные контекста, вызвав getContext(). Также обратите внимание, что геттеры, определенные в JS-коде, не доступны на бэкенде, поэтому во время обработки директив на сервере, директивы включающие свойства с геттерами, будут пропущены.

В таких случаях, когда вы хотите, чтобы они участвовали в SSR, вы можете определить их, используя функцию wp_interactivity_state как примитивные булевые значения, а затем просто переопределить их в JS, назначив функцию тому же имени.

7.2) Объект события в действиях

Когда мы назначаем наш обработчик события для какого-либо элемента в чистом JavaScript, например, с помощью .addEventListener('click'), этот обработчик будет получать первым аргументом объект типа Event. Сам объект будет различаться в зависимости от конкретного действия, но во всех случаях он реализует общий интерфейс Event.

Действия в WP Interactivity API также получают этот объект, поэтому вы можете использовать его по своему усмотрению, например, чтобы получить текущий элемент, как event.target:

import {store} from '@wordpress/interactivity';

store("my-accordion", {
    actions: {
        toggle: (event) => {
            let clickedHTMLElement = event.target;
            // clickedHTMLElement.innerHTML = 'x';
        }
    }
});

7.3) Директива цикла: wp-each

Это продвинутая директива, которая вам понадобится только в случаях, когда интерфейс требует динамического создания и удаления элементов. Примером является классический список дел, где помимо редактирования элементы могут быть добавлены и удалены.

Эта директива позволяет определить шаблон, который будет применен ко всем элементам списка. Идея заключается в том, что при изменении связанной переменной в JS элементы будут автоматически синхронизироваться.

Давайте рассмотрим этот пример:

<?php

$context = [
	'list' => [
		[
			"id"    => "en",
			"value" => "hello"
		],
		[
			"id"    => "es",
			"value" => "hola"
		],
		[
			"id"    => "pt",
			"value" => "olá"
		]
	]
];

?>

<div class="accordion"
     data-wp-interactive="my-accordion"
     data-wp-context='<?php
     echo json_encode( $context ); ?>'>
    <ul>
        <template
                data-wp-each--item="context.list"
                data-wp-each-key="context.item.id">
            <li data-wp-text="context.item.value"></li>
        </template>
    </ul>
    <button data-wp-on--click="actions.removeLastItem">Remove last item</button>
</div> 
import {store, getContext} from '@wordpress/interactivity';

const {state} = store("my-accordion", {
    actions: {
        removeLastItem: (event) => {
            let context = getContext();

            context.list.pop();
        }
    }
});

В этом примере у нас есть список элементов, каждый из которых имеет идентификатор и значение. С помощью специального тега Template и директивы data-wp-each мы определяем цикл по списку. В имени директивы мы указываем имя элемента, в нашем случае item (data-wp-each--item=), а в качестве значения определяем сам список, который может быть как переменной контекста, так и состояния.

Обратите внимание, что мы используем директиву wp-each-key, чтобы указать на уникальный идентификатор элемента, благодаря чему Interactivity API будет синхронизировать разметку с переменной.

Наш цикл будет обработан во время SSR и заменен разметкой, заполненной на основе элементов в массиве. Но в отличие от обычного цикла PHP, он будет поддерживать контекстную переменную списка, определенную в директиве wp-each, в полном соответствии с разметкой, поэтому, когда мы удалим последний элемент массива в обработчике действия, API автоматически удалит целевой элемент в разметке.

7.4) Межблочное взаимодействие

Как мы уже упоминали, все переменные состояния блоков являются общедоступными, поэтому к ним можно обращаться из других блоков. Но как же мы можем это сделать?

Чтобы получить состояние другого блока, мы должны вызвать функцию store с именем целевого блока, но без передачи второго аргумента, как мы делали ранее.

Ниже мы покажем, как получить переменную состояния someData из блока my-another-block. Этот код может быть размещен где угодно, например, внутри любого обработчика нашего аккордеона.

import {store} from '@wordpress/interactivity';

// ....

console.log(store("my-another-block").state.someData);

8. Заключение

Добавление Interactivity API в WordPress представляет собой значительный прогресс в экосистеме WordPress. Хотя API было выпущена недавно и еще не получило широкого распространения, в ближайшем будущем оно непременно сыграет ключевую роль в WordPress разработке.

Этот встроенный инструмент поможет разработчикам легко создавать интерактивные интерфейсы. Благодаря унифицированному подходу создатели плагинов и тем смогут разрабатывать собственные интерактивные блоки, которые будут взаимодействовать между собой независимо от конкретного поставщика.

Мы надеемся, что данный материал был полезен и что вы освоили все основные аспекты Interactivity API. Удачной разработки!

Теги:
Хабы:
Всего голосов 5: ↑5 и ↓0+7
Комментарии1

Публикации

Работа

Ближайшие события