Кеширование WordPress Gutenberg

При оптимизации скорости наших сайтов со стороны сервера кэширование входит в число наиболее важных задач, которые необходимо выполнить правильно. В этой статье Leonardo Losoviz исследует архитектуру, основанную на self-rendering компонентах и SSR, и анализирует, как реализовать ее для сайтов на WordPress с помощью редактора Gutenberg.

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

При генерации HTML содержимого страницы, если она содержит динамические элементы, основанные на пользовательских данных, например, печать приветственного сообщения «Здравствуйте, {{Имя пользователя}}!» для вошедшего в систему пользователя, страница не может быть кэширована. В противном случае, если Питер заходит на сайт, а HTML кэшируется, все пользователи будут видеть «Привет, Питер!».

Плагины кэширования для WordPress обычно предлагают отключить кэширование, когда пользователь вошел в систему, как показано ниже для плагина WP Super Cache :

Отключено кэширование для известных пользователей в WP Super Cache
WP Super Cache рекомендует отключить кеширование для авторизованных пользователей.

Отключение кэширования для зарегистрированных пользователей нежелательно и его следует избегать, поскольку даже если объем HTML-кода с пользовательскими данными минимален по сравнению со статическим содержимым на странице, все равно ничего не будет кэшировано. Причина в том, что кешируемая сущность — это страница, а не отдельные фрагменты HTML-кода на странице, поэтому, если включить одну строку кода, которую нельзя кэшировать, то кэш не будет создан для целой страницы. Это ситуация «все или ничего».

Чтобы решить эту проблему, мы можем спроектировать наше приложение таким образом, чтобы избежать рендеринга HTML-кода с пользовательским состоянием на стороне сервера, и рендерерить его только на стороне клиента, после получения необходимых данных через API (в основном на REST или GraphQL). Удаляя пользовательские данные из кода, который собирается на сервере, эта страница может быть кэширована, даже если пользователь вошел в систему.

В этой статье мы рассмотрим следующие вопросы:

  • Как мы идентифицируем те части кода, которые требуют пользовательские данные, изолируем их от остальной части страницы и заставим отрисовываться только на стороне клиента?
  • Как это можно реализовать для сайтов на WordPress с редактором Gutenberg?

Gutenberg приносит в WordPress компоненты

Gutenberg — это редактор для WordPress на основе JavaScript (точнее, это редактор на основе React, инкапсулирующий библиотеки React за глобальным wpобъектом), который был выпущен вместе с WordPress. С помощью своего drag-and-drop интерфейса Gutenberg полностью преобразит процесс создания контента для WordPress и, на более позднем этапе в будущем, процесс создания сайтов. Он заменит текущий процесс создания страницы с помощью шаблонов (header.php, index.php, sidebar.php, footer.php) и содержимого страницы с помощью единого фрагмента кода HTML на процесс создания компонентов. Эти компоненты могут быть размещены в любом месте на странице и могут управлять своей собственной логикой, загружать свои собственные данные и самостоятельно выполнять рендеринг.

Чтобы оценить предстоящие перемены в WordPress: от этого

Страница содержит шаблоны с кодом HTML
В настоящее время страницы создаются с помощью шаблонов PHP

К этому:

Страница содержит автономные компоненты
В ближайшем будущем страницы будут создаваться путем размещения в них самостоятельных компонентов

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

Cвязь между страницами и компонентами

Как упоминалось ранее, кэшируемая сущность является страницей. Следовательно, нам нужно оценить, как компоненты размещаются на странице, чтобы максимизировать готовность страницы к кешированию. Основываясь на зависимости компонентов от пользовательских данных, мы можем в общих чертах разделить страницы на следующие 3 группы:

  1. Страницы без использования какого-либо пользовательских данных.
  2. Страницы с элементами пользовательских данных, такие как домашняя страница с приветствием пользователя («Добро пожаловать, Питер!»), или страница архива со списком записей с кнопкой «Мне нравится» под каждым сообщением, которая окрашена в синий цвет, если вошедший в систему пользователь лайкнул этот пост.
  3. Страницы, непосредсвенно основанные на статусе пользователя, содержание которых напрямую зависит от пользователя, вошедшего в систему, например «Мои записи» на странице «Мой профиль».

Компоненты, с другой стороны, могут быть просто классифицированы как требующие пользовательских данных или нет. Поскольку архитектура рассматривает компонент как единицу для построения страницы, компонент обладает способностью знать, требуется ли ему состояние пользователя или нет. Следовательно, компонент <WelcomeUser />, который отображает «Добро пожаловать, Питер!», знает, что ему требуется данные пользователя, а
компонент, например, <WhoWeAre /> знает, что ему они не нужны.

Затем нам нужно разместить компоненты на странице, и в зависимости от комбинации страницы и компонента, требующего пользовательского состояния или нет, мы можем установить правильную стратегию для кэширования страницы и для предоставления контента пользователю как можно скорее. У нас есть следующие случаи:

1. СТРАНИЦЫ БЕЗ КАКОГО-ЛИБО ПОЛЬЗОВАТЕЛЬСКОГО СОСТОЯНИЯ

Они могут быть кэшированы без проблем.

  • Страница кэшируется => Она не может получить доступ к состоянию пользователя.
  • Компоненты, ни один из которых не требует пользовательского состояния, отображаются на сервере.
Страница без состояния пользователя
Страница без пользовательского состояния может содержать только компоненты без пользовательского состояния.

2. СТРАНИЦЫ С ИСПОЛЬЗОВАНИЕМ ПОЛЬЗОВАТЕЛЬСКОГО СОСТОЯНИЯ

Мы могли бы сделать страницу либо требующей пользовательские данные или нет. Если мы заставляем страницу требовать пользовательские данные, то она не может быть кэширована, что является упущенной возможностью, когда большая часть контента на странице статична. Следовательно, мы бы предпочли, чтобы страница не требовала пользовательского состояния, а компоненты, требующие пользовательского состояния, которые размещаются на странице, например <WelcomeUser />на домашней странице, выполняются с отложенной загрузкой: на стороне сервера отображается пустой контейнер, а компонент рендерится на стороне клиента, после получения его данных из API.

С таким подходом весь статический контент на странице будет отрисовываться немедленно на стороне сервера (SSR), а элементы с состоянием пользователя после некоторой задержки отрендерятся на стороне клиента (CSR).

  • Страница кэшируется => Она не может получить доступ к состоянию пользователя.
  • Компоненты, не требующие пользовательского состояния, отображаются на сервере.
  • Компоненты, требующие пользовательского состояния, отображаются в клиенте.
Страница с битами пользовательского состояния
Страница с битами пользовательского состояния содержит компоненты CSR с пользовательским состоянием и компоненты SSR без пользовательского состояния.

3. СТРАНИЦЫ СО СТАТУСОМ ПОЛЬЗОВАТЕЛЯ

Если библиотека или фреймворк разрешает рендеринг только на стороне клиента, то мы должны следовать тому же подходу, что и в # 2: не заставлять страницу требовать пользовательского состояния и добавлять компонент, например <MyPosts />, для самостоятельного рендеринга в браузере клиента.

Однако, поскольку основная цель страницы — показать пользовательский контент, заставлять пользователя ждать загрузки этого контента на втором этапе не является идеальным. Давайте рассмотрим это на примере: пользователь, который еще не вошел в систему, получает доступ к странице «Редактировать мой профиль». Если сайт формирует контент на сервере, так как пользователь не вошел в систему, сервер немедленно перенаправит его на страницу входа. Вместо этого, если содержимое формируется на клиенте через API, пользователю сначала будет представлено сообщение о загрузке, и только после возвращения ответа от API пользователь будет перенаправлен на страницу входа, что замедляет работу.

Следовательно, нам лучше использовать библиотеку или фреймворк, поддерживающий рендеринг на стороне сервера, и мы заставляем страницу требовать пользовательского состояния (что делает ее не кешируемой):

  • Страница не кэшируется => Она может получить доступ к состоянию пользователя.
  • Компоненты, как требующие, так и не требующие пользовательского состояния, отображаются на сервере.
Страница со статусом пользователя
Страница с пользовательским состоянием содержит компоненты SSR как с пользовательским состоянием, так и без него. 

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

if (component requires user state and page can’t access user state) {
    render component in client
}
else {
    render component in server
}

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

Рендеринг компонентов на стороне клиента / сервера в Gutenberg

В Gutenberg компоненты, которые могут быть встроены в страницу, называются «блоками». Gutenberg поддерживает два типа блоков, статические и динамические:

  • Статические блоки создают свой HTML-код уже в клиенте (когда пользователь взаимодействует с редактором) и сохраняют его внутри содержимого публикации. Следовательно, они являются клиентскими блоками на основе JavaScript.
  • Динамические блоки — могут динамически изменять свое содержимое, например, блок последних сообщений, поэтому они не могут сохранить вывод HTML внутри содержимого публикации. Следовательно, в дополнение к созданию своего HTML-кода на стороне клиента, они также должны генерировать его с сервера во время выполнения через функцию PHP (которая определяется в параметреrender_callbackпри регистрации блока во внутренней функции через функциюregister_block_type).

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

if (block requires user state and page can’t access user state) {
    render block in client through JavaScript
}
else {
    render (dynamic) block in server through PHP code
}

К сожалению, реализация двойной клиент-серверной функциональности не обходится без трудностей: SSR Gutenberg не изоморфен, то есть не позволяет одной кодовой базе генерировать выходные данные как для клиентского, так и для серверного кода. Следовательно, разработчикам необходимо поддерживать 2 кодовые базы, одну на PHP и одну на JavaScript, что далеко не оптимально.

Gutenberg также реализует компонент <ServerSideRender />, однако использовать его не рекомендуется: этот компонент предназначен не для повышения скорости сайта и предоставления немедленного ответа пользователю, а для обеспечения совместимости с устаревшим кодом, таким как шорткоды.

Как это объяснено в документации :

«ServerSideRender следует рассматривать как запасной или устаревший механизм, он не подходит для разработки новых функций. «Новые блоки должны создаваться в сочетании с любыми необходимыми конечными точками REST API, чтобы JavaScript мог использоваться для визуализации на стороне клиента в редакторе. Это дает лучший пользовательский опыт, вместо использования PHP render_callback. Логика, необходимая для рендеринга, должна быть включена в конечную точку, так что как логика JavaScript на стороне клиента, так и логика PHP на стороне сервера должны требовать минимального количества отличий».

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

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

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

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

function get_pages_with_user_state() {

    return apply_filters(
        'get_pages_with_user_state',
        array()
    );
}

После чего мы реализуем хуки с соответствующими страницами, например так:

// ID of the pages, retrieved from the WordPress admin
define ('MYPOSTS_PAGEID', 5);
define ('ADDPOST_PAGEID', 8);

add_filter('get_pages_with_user_state', 'get_pages_with_user_state_impl');
function get_pages_with_user_state_impl($pages) {
    
  $pages[] = MYPOSTS_PAGEID;

  // "Add Post" may not require user state!
  // $pages[] = ADDPOST_PAGEID;
    
  return $pages;
}

Обратите внимание, что нам может не потребоваться добавлять пользовательское состояние для страницы «Добавить запись» (что делает эту страницу кэшируемой), даже если эта страница требует подтверждения того, что пользователь вошел в систему при отправке формы для создания контента на сайте. Это связано с тем, что на странице «Добавить запись» может просто отображаться пустая форма, не требующая никакого пользовательского состояния. Тогда отправка формы будет операцией POST, которая не может быть кэширована ни в коем случае (кэшируются только запросы GET).

Отключение кэширования страниц с состоянием пользователя в WP Super Cache

Мы настраиваем приложение, чтобы отключить кэширование для тех страниц, которые требуют пользовательского состояния (и кэшировать все остальное.) Сделаем это для плагина WP Super Cache, занеся URI этих страниц в исключения в настройках плагина:

Настройки WP Super Cache для отключения кеширования строк в черном списке
Мы можем отключить кэширование URL-адресов, содержащих определенные строки, в WP Super Cache.

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

function get_rejected_strings() {

  $rejected_strings = array();
  $pages_with_user_state = get_pages_with_user_state();
  foreach ($pages_with_user_state as $page) {

      // Calculate the URI for that page to the list of rejected strings
      $path = substr(get_permalink($page), strlen(home_url()));
      $rejected_strings[] = $path;
  }

  return $rejected_strings;
}

Затем мы должны добавить отклоненные строки в файл конфигурации WP Super Cache, расположенный в wp-content / wp-cache-config.php, обновив значение записи $cache_rejected_uriс помощью нашего списка путей:

function set_rejected_strings_in_wp_super_cache() {

  if ($rejected_strings = get_rejected_strings()) {

    // Keep the original values in
    $rejected_strings = array_merge(
      array('wp-.*\\\\.php', 'index\\\\.php'),
      $rejected_strings
    );
      
    global $wp_cache_config_file;
    $cache_rejected_uri = "array('".implode("', '", $rejected_strings)."')";
    wp_cache_replace_line('^ *\$cache_rejected_uri', "\$cache_rejected_uri = " . $cache_rejected_uri . ";", $wp_cache_config_file);
  }
}

После выполнения функции set_rejected_strings_in_wp_super_cacheнастройки будут обновлены новыми отклоненными строками:

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

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

Отключено кэширование для известных пользователей в WP Super Cache
Больше не нужно отключать кеширование для зарегистрированных пользователей!

Заключение

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

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

Наконец, мы объяснили, что решение может быть интегрировано в плагин кэширования с помощью специального скрипта для автоматического создания списка страниц, и создали код для плагина WP Super Cache.

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

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Scroll to top