Gutenberg — это редактор по умолчанию для WordPress. Он позволяет создавать и оформлять контент с помощью отдельных блоков для текста, изображений, видео и других элементов сайта с помощью интерфейса drag-and-drop. Этот подход повышает гибкость и возможности дизайна WordPress. Давайте разберемся как устроен Gutenberg для headless WordPress?
Данное руководство объясняет, как парсить контент Gutenberg в HTML с помощью REST API WordPress на статическом сайте Next.js.
Необходимые условия
Для того чтобы следовать руководству, вам потребуется:
- Node.js и npm (Node Package Manager) или yarn, установленные на вашем компьютере
- Базовые знания JavaScript
- Сайт WordPress с несколькими опубликованными записями
Получение контента Gutenberg с помощью REST API
Чтобы взаимодействовать с вашим сайтом WordPress программно и получать контент, структурированный блоками Gutenberg, вы используете REST API WordPress или плагин WPGraphQL. Эти инструменты позволяют вам получать контент WordPress в формате JSON.
Чтобы включить доступ к данным JSON через REST API, измените настройки постоянных ссылок WordPress с «Обычный» на другой вариант. Это позволяет осуществлять доступ к API через структурированный URL, например:
https://yoursite.com/wp-json/wp/v2
Делая запросы к API по этому URL-адресу, вы можете программно получать различную информацию и выполнять операции на своем сайте WordPress. Например, вы можете получить список записей, отправив GET-запрос на:
https://yoursite.com/wp-json/wp/v2/posts
Это вернет JSON-объект, содержащий информацию о записях на вашем сайте WordPress, включая заголовки, содержимое, данные автора и многое другое.
Парсинг блоков Gutenberg в HTML
При получении записей с сайта WordPress, который использует редактор Gutenberg, хранимый контент в базе данных может представлять собой смесь HTML и JSON-метаданных для описания различных типов блоков, таких как цитаты и галереи. Например:
<!-- wp:quote {"className":"inspirational-quote","style":{"typography":{"fontSize":"large"}}} -->
<blockquote class="wp-block-quote inspirational-quote has-large-font-size"><p>“The journey of a thousand miles begins with one step.”</p><cite>Lao Tzu</cite></blockquote>
<!-- /wp:quote -->
<!-- wp:gallery {"ids":[34,35],"columns":2,"linkTo":"none","sizeSlug":"medium","className":"custom-gallery"} -->
<ul class="wp-block-gallery columns-2 is-cropped custom-gallery"><li class="blocks-gallery-item"><figure><img src="http://example.com/wp-content/uploads/2021/09/image1-300x200.jpg" alt="A breathtaking view of the mountains" class="wp-image-34"/></figure></li><li class="blocks-gallery-item"><figure><img src="http://example.com/wp-content/uploads/2021/09/image2-300x200.jpg" alt="Serene lakeside at dawn" class="wp-image-35"/></figure></li></ul>
<!-- /wp:gallery -->
Этот фрагмент иллюстрирует два блока Gutenberg: цитату и галерею. Каждый из них дополнен JSON-метаданными, заключенными в HTML-комментарии. Метаданные определяют такие атрибуты, как имена классов, стили и другие конфигурации, относящиеся к представлению блока.
Когда вы получаете эти блоки через REST API WordPress или WPGraphQL, WordPress обрабатывает их, преобразуя комбинацию HTML и JSON-метаданных в полностью отрисованные HTML-элементы, которые вы можете напрямую внедрять на веб-страницы. Преобразованный HTML для приведенных выше блоков будет выглядеть следующим образом:
<blockquote class="wp-block-quote inspirational-quote has-large-font-size"><p>“The journey of a thousand miles begins with one step.”</p><cite>Lao Tzu</cite></blockquote>
<ul class="wp-block-gallery columns-2 is-cropped custom-gallery">
<li class="blocks-gallery-item"><figure><img loading="lazy" src="http://example.com/wp-content/uploads/2021/09/image1-300x200.jpg" alt="A breathtaking view of the mountains" class="wp-image-34" sizes="(max-width: 300px) 100vw, 300px" /></figure></li>
<li class="blocks-gallery-item"><figure><img loading="lazy" src="http://example.com/wp-content/uploads/2021/09/image2-300x200.jpg" alt="Serene lakeside at dawn" class="wp-image-35" sizes="(max-width: 300px) 100vw, 300px" /></figure></li>
</ul>
Для разработчиков, создающих разделенные (decoupled) или безголовые (headless) приложения с использованием JavaScript-фреймворков, таких как Next.js, этот метод предлагает простой способ отображения контента путем прямого внедрения HTML на страницу с помощью свойства dangerouslySetInnerHTML
для отрисовки разметки.
Вот синтаксис использования этого свойства:
<div dangerouslySetInnerHTML={{ __html: <raw_html_string> }} />
Парсинг контента блоков Gutenberg в статическом сайте Next.js
В этом разделе мы разберемся, как извлечь контент WordPress в проект Next.js, а затем преобразовать блоки Gutenberg в HTML.
Для начала создадим функцию для получения записей с вашего сайта WordPress. Откройте файл src/page.js
в вашем проекте и замените его содержимое следующим фрагментом кода:
const getWpPosts = async () => {
const res = await fetch('https://yoursite.com/wp-json/wp/v2/posts');
const posts = await res.json();
return posts;
};
Эта асинхронная функция выполняет запрос к API WordPress REST. Она получает все записи, доступные на вашем сайте, и возвращает их в виде массива.
Далее давайте используем полученные записи внутри простого компонента страницы Next.js, выведя их в консоль и отобразив базовое приветствие:
const page = async () => {
const posts = await getWpPosts();
console.log(posts);
return (
<div>
<h1>Hello World</h1>
</div>
);
};
export default page;
Запустив проект с помощью команды npm run dev
, вы увидите надпись «Hello World» и логи полученных записей в терминале.
Данные JSON, представляющие информацию о каждом посте Gutenberg, включают различные поля, среди которых поля контента (content
) и краткого содержания (excerpt
) возвращаются как отформатированные в HTML блоки Gutenberg.
Для правильного отображения этого HTML-контента в Next.js мы используем свойство dangerouslySetInnerHTML
:
const page = async () => {
const posts = await getWpPosts();
return (
<>
<h1>Headless Blog</h1>
<div>
{posts.map((post) => (
<Link href={'/blog/' + post.id} key={post.id}>
<h2>
{post.title.rendered} <span>-></span>
</h2>
<div dangerouslySetInnerHTML={{ __html: post.excerpt.rendered }} />
</Link>
))}
</div>
</>
);
};
export default page;
В этом обновленном компоненте мы выполняем цикл по массиву полученных записей для создания списка кратких содержаний. Каждое краткое содержание обернуто компонентом Link
для навигации, отображая заголовок записи и фрагмент ее содержимого.
Свойство dangerouslySetInnerHTML
используется для парсинга и отображения HTML-контента, содержащегося в поле excerpt.rendered
.
Далее создайте файл blog/[id]/page.js
внутри каталога app
. В Next.js папки используются для определения маршрутов. Таким образом, создавая папку blog
, вы определяете маршрут /blog/
. В сочетании с динамической маршрутизацией это позволяет создавать маршруты для каждой отдельной записи.
Каждая запись имеет ID. Вы используете этот ID для создания ее уникального маршрута /blog/{post_id}
в вашем приложении. Добавьте следующий код:
import Link from 'next/link';
export async function generateStaticParams() {
const res = await fetch('https://yoursite.com/wp-json/wp/v2/posts');
const posts = await res.json();
return posts.map((post) => {
return {
params: {
id: post.id.toString(),
},
};
});
}
export async function getPost(id) {
const response = await fetch('https://yoursite.com/wp-json/wp/v2/posts/' + id);
const post = await response.json();
return post;
}
Функция generateStaticParams
статически генерирует маршруты во время сборки на основе соответствующего ID, возвращаемого для каждой записи. Функция getPost
получает данные Gutenberg из REST API для записи с переданным ID.
В предыдущем разделе были показаны примеры разобранных данных Gutenberg, возвращаемых из REST API для записи. На данный момент нас интересует только поле content.rendered
:
[
{
...
"content": {
"rendered" : "\n<p>Fire, a primal force, captivates with its <strong>flickering flames</strong>, evoking both awe and caution. Its <quote>dance</quote> symbolizes destruction and renewal, consuming the old to make way for the new. While it warms our homes and hearts, fire demands respect for its power to devastate.</p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"250\" height=\"148\" src=\"https://img.example.com/wp-content/uploads/2024/02/burningbuilding.jpg\" alt=\"\" class=\"wp-image-14\"/></figure>\n\n\n\n<p>In ancient times, fire was a beacon of light and warmth, essential for survival. Today, it remains a symbol of human ingenuity and danger. From the comforting glow of a hearth to the destructive fury of wildfires, fire’s dual nature reminds us of our fragile relationship with the elements.</p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https://img.example.com/premium-photo/painting-burning-building-illuminated-by-bright-flames-night_168058-249.jpg?w=1380\" alt=\"\"/></figure>\n\n\n\n<p>You can check out other articles on our blog:</p>\n\n\n\n<ul>\n<li><a href=\"https://yoursite.com/?p=6\">Lorem Ipsum: Beginnings</a></li>\n\n\n\n<li><a href=\"https://yoursite.com/?p=9\">Lorem Ipsum: Act 2</a></li>\n\n\n\n<li><a href=\"https://yoursite.com/?p=11\">Lorem Ipsum: Act 3</a></li>\n</ul>\n"
},
...
}
]
Это поле содержит необработанный HTML-код записи. Его можно напрямую отрисовать с помощью свойства dangerouslySetInnerHTML
следующим образом:
<div dangerouslySetInnerHTML={{ __html: <raw_html_string> }} />
Далее вы можете обработать данные, разобрав внутренние ссылки и изменив размер изображений. Установите пакет html-react-parser
для упрощения процесса разбора тегов:
npm install html-react-parser --save
Добавьте следующий код в файл blog/[id]/page.js
:
import parse, { domToReact } from "html-react-parser";
export function fixInternalLinks(html_string) {
const pattern = /href="https:\/\/yoursite.com\/\?p=(\d+)"/g;
const replacement = 'data-internal-link="true" href="/blog/$1"';
return html_string.replace(pattern, replacement);
}
export function parseHtml(html) {
// Replace 2+ sequences of '\n' with a single '<br />' tag
const _content = html.replace(/\n{2,}/g, '<br />');
const content = fixInternalLinks(_content);
const options = {
replace: ({ name, attribs, children }) => {
// Convert internal links to Next.js Link components.
const isInternalLink =
name === "a" && attribs["data-internal-link"] === "true";
if (isInternalLink) {
return (
<Link href={attribs.href} {...attribs}>
{domToReact(children, options)}
</Link>
);
} else if (name === "img") {
attribs["width"] = "250";
attribs["height"] = "150";
return (
<img {...attribs}/>
);
}
},
};
return parse(content, options);
}
Функция fixInternalLinks()
использует регулярное выражение для поиска ссылок на записи вашего сайта WordPress из строки HTML. В исходном HTML-коде вы можете увидеть, что запись содержит тег <ul>
со ссылками на другие записи на вашем сайте. Эта функция заменяет эти ссылки внутренними ссылками на маршруты вашего статического сайта.
Функция parseHtml()
находит последовательности дополнительных переносов строк (\n
) и заменяет их тегами <br />
. Она также обнаруживает внутренние ссылки и преобразует теги a
в компоненты Link
. Затем эта функция изменяет размер изображений с помощью атрибутов тегов.
Создание основного интерфейса
Чтобы сгенерировать основной интерфейс для каждого динамического маршрута, добавьте следующий код:
export default async function Post({ params }) {
const post = await getPost(params.id);
const content = parseHtml(post.content.rendered);
return (
<>
<h1>
{post.title.rendered}
</h1>
<div>{content}</div>
</>
);
}
После разбора необработанного HTML-кода из данных Gutenberg код возвращает JSX, представляющий форматированный интерфейс страницы.
Запуск проекта
Запустив проект, на главной странице вы увидите список записей вашего сайта WordPress. Кроме того, при переходе по отдельным записям вы увидите правильно отрисованное содержимое блоков Gutenberg.
Заключение. Gutenberg для headless WordPress
Это руководство объяснило, как эффективно интегрировать и парсить контент блоков Gutenberg в HTML с помощью API WordPress. Это позволяет отображать любой тип контента на вашем фронтенде при использовании headless WordPress.
Приглашаю вас прокачать свои навыки в веб разработке записавшись в наш Клуб Genius.Courses а так же записаться на наши курсы на UDEMY:
Погрузитесь в увлекательный мир веб разработки и дизайна с моими эксклюзивными курсами на Udemy! Я их разработал с душой и учел все нюансы. Сейчас активированы скидки до 90%! Получите доступ к знаниям высокого качества от $9!