SEO-оптимизация SPA
Что такое SEO?
Search Engine Optimization — это процесс оптимизации сайта (а в нашем случае веб-приложения) для улучшения его видимости в поисковых системах.
В сети существует множество сайтов, и пользователю нужно быстро получать релевантную информацию. Для этого поисковые роботы индексируют сайты по специальным метрикам, которые мы рассмотрим ниже.
Процесс поисковой индексации можно разделить на три этапа:
- Краулинг: сбор данных посредством перехода по страницам сайта и их анализа.
- Индексация: данные обрабатываются и добавляются в индекс поисковой системы — упрощённо, это большая оптимизированная для поиска база данных.
- Ранжирование: происходит оценка страницы на основе специальных метрик. При пользовательском запросе система выбирает наиболее релевантные сайты и выдаёт их.
А зачем вообще SEO?
Это необходимо как для бизнеса, чтобы обойти конкурентов, так и для улучшения пользовательского опыта, чтобы на первой странице поисковой выдачи оказывались ожидаемые и нужные результаты.
По каким метрикам определяется релевантность ресурса?
Технические метрики
К этому типу относятся:
- Скорость загрузки.
- Адаптивность: например, движок Google использует mobile-first подход, то есть мобильная версия приоритетнее.
- Доступность для индексации: наличие
robots.txt,sitemap.xml, canonical-ссылок и т.д. - Структура и читаемость URL.
Контентные метрики
Важно содержимое сайта. К этим метрикам относятся:
- Ключевые слова и семантика: например, если пользователь ищет “купить iPhone 15”, роботы проверяют наличие этих слов в
<title>,<h1>,<meta name="description">и основном тексте. - Уникальность контента.
- Доступность для пользователей: использование
alt-атрибутов для изображений,aria-атрибутов.
Для продвинутых существует микроразметка. Пример для запроса с iPhone 15:
<script type="application/ld+json">
{
"@context": "https://schema.org/",
"@type": "Product",
"name": "iPhone 15",
"description": "Новый iPhone 15 с улучшенной камерой.",
"brand": {
"@type": "Brand",
"name": "Apple"
}
}
</script>
Поведенческие метрики
Эти метрики связаны с тем, как пользователи взаимодействуют с сайтом:
- Время на сайте: чем дольше пользователи остаются, тем выше релевантность.
- Показатель отказов.
- Глубина просмотра: пользователи должны просматривать несколько страниц за визит.
- CTR (Click-Through Rate): высокий CTR в поисковой выдаче указывает на релевантность заголовка и описания страницы.
Внешние метрики
Эти метрики связаны с авторитетностью сайта и его популярностью в интернете:
- Обратные ссылки: чем больше качественных ссылок с авторитетных сайтов (например, Forbes или Wikipedia), тем выше релевантность.
- Доверие домена: например, хостинг на GitHub Pages повышает доверие и шансы на высокую позицию в выдаче.
Что такое SPA?
Single Page Application — современный подход к написанию веб-приложений, который становится всё популярнее из-за простоты и скорости разработки и поддержки.
Суть в следующем: приложение загружает одну HTML-страницу без значимого контента, а затем её содержимое динамически изменяется по мере взаимодействия с пользователем.
Для дальнейшего исследования мы возьмём React и будем обсуждать, как поднимать React-приложения в поисковой выдаче.
Готовь сани летом…
Идеально, если архитектура приложения изначально учитывает возможности оптимизации, о которых мы поговорим ниже. Лучше продумывать такие вещи заранее, и мы будем рассматривать именно такие случаи. Однако существуют более гибкие и простые (но менее функциональные) решения для интеграции в уже готовые проекты.
Server-Side Rendering (SSR)
Теория
Основная проблема SPA — отсутствие контента на старте страницы. Многие поисковые роботы плохо исполняют JavaScript, из-за чего страница индексируется в том виде, в котором она “скормлена” роботу:
<!DOCTYPE html>
<html>
<head>
<!-- Какие-то стили -->
</head>
<body>
<div id="root"></div>
<script src="index.js"></script>
</body>
</html>
Непонятно, как определить, релевантен ли ресурс пользователю.
Отступление о мета-тегах
Можно задать вопрос: почему бы не добавить мета-теги в <head>? Ведь он есть, и можно просто отредактировать app.html?
Это имеет смысл и сработает, но только если у вас действительно одна страница. Мета-теги должны быть актуальны для каждой страницы.
Дальше мы обсудим технологии, которые позволяют динамически добавлять мета-теги.
Вернемся к проблеме пустой страницы. Текущая схема работы SPA:
Как получить контент сразу, не дожидаясь полной загрузки страницы? Отличное решение — шаблонизаторы, но для SPA используется SSR.
Создадим сервер-посредник между бэкендом и клиентом. Метафреймворк будет взаимодействовать с сервером. Схематично:
Таким образом, поисковые роботы получат уже пререндеренную страницу и смогут её корректно обработать.
Как имплементировать в React?
Самый популярный выбор — Next.js. Это стабильное решение, которому доверяют крупные проекты.
Next.js предоставляет два типа компонентов: 'use server' для серверных и 'use client' для клиентских. Серверные компоненты имеют доступ к функции getServerSideProps(context), которая позволяет взаимодействовать с API и возвращать данные с бэкенда.
Этот подход удобен для работы с cookies или управления кэшированием.
Примеры:
Пример 1: простая передача данных через props
export async function getServerSideProps(context) {
const res = await fetch('https://abaunda.org/post/69');
const data = await res.json();
return {
props: {
data,
},
};
}
export default function Page({ data }) {
return (
<div>
<h1>Данные с сервера:</h1>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
}
Пример 2: манипуляция заголовками запросов
export async function getServerSideProps(context) {
const userAgent = context.req.headers['user-agent'];
return {
props: {
userAgent,
},
};
}
Пример 3: манипуляция заголовками ответов
export async function getServerSideProps(context) {
context.res.setHeader('Cache-Control', 'public, s-maxage=10, stale-while-revalidate=59');
const res = await fetch('https://abaunda.org/post/69');
const data = await res.json();
return {
props: {
data,
},
};
}
Static Site Generation (SSG)
Теория
SSR хорош для динамических приложений, но если контент меняется редко (например, блоги или лендинги), SSR может быть избыточным.
Зачем рендерить сложное приложение каждый раз, если содержимое не меняется? Разумнее собрать проект в статические HTML-страницы, которые отправляет сервер. Это и есть Static Site Generation (SSG).
Главное преимущество SSG — экстремально высокая скорость работы. Однако для динамических приложений лучше использовать SSR.
Как имплементировать в React
Next.js поддерживает SSG через функцию getStaticProps(), которая выполняется на этапе сборки приложения.
Пример использования:
export async function getStaticProps() {
const res = await fetch('https://abaunda.org/posts');
const posts = await res.json();
return {
props: {
posts,
},
};
}
export default function Blog({ posts }) {
return (
<div>
<h1>Блог</h1>
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
);
}
SSR + SSG
Некоторые фреймворки, включая Next.js, позволяют комбинировать SSR и SSG в одном проекте для гибкости. Всё зависит от того, как вы описываете компоненты.
Incremental Static Rendering (ISR)
Для случаев, когда данные нужно обновлять чаще, но SSR кажется сложным, Next.js поддерживает ISR для SSG через параметр revalidate:
export async function getStaticProps() {
const res = await fetch('https://abaunda.org/posts');
const posts = await res.json();
return {
props: {
posts,
},
revalidate: 10, // Пересобирать страницу каждые 10 секунд
};
}
export default function Blog({ posts }) {
return (
<div>
<h1>Блог</h1>
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
);
}
Страница будет пересобираться каждые 10 секунд в фоновом режиме.
Динамические мета-теги
Теперь, когда с контентом проблем нет, разберёмся с мета-тегами. Они должны быть уникальными для каждой страницы, поэтому нужен способ их динамического обновления.
Мы рассмотрим решения для React, хотя принципы схожи для большинства фреймворков.
До React 19
Для динамического управления мета-тегами используется библиотека react-helmet. Пример:
import { Helmet } from 'react-helmet';
export default function Post({ post }) {
return (
<div>
<Helmet>
<title>{post.title}</title>
<meta name="description" content={post.description} />
<meta property="og:title" content={post.title} />
<meta property="og:description" content={post.description} />
<meta property="og:image" content={post.image} />
</Helmet>
<h1>{post.title}</h1>
<p>{post.content}</p>
</div>
);
}
Это простой и проверенный способ. Для SSR рекомендуется использовать форк react-helmet-async, который требует обёртывания в специальный провайдер.
React 19
В React 19 добавлена встроенная поддержка динамических мета-тегов. Пример:
export default function Post({ post }) {
return (
<div>
<title>{post.title}</title>
<meta name="description" content={post.description} />
<meta property="og:title" content={post.title} />
<meta property="og:description" content={post.description} />
<meta property="og:image" content={post.image} />
<h1>{post.title}</h1>
<p>{post.content}</p>
</div>
);
}
Мета-теги могут сделать компонент “грязнее”. Если это беспокоит, можно использовать фрагмент или react-helmet.
Важно! Убедитесь, что используется React 19 или выше, иначе мета-теги окажутся в корневом компоненте, а не в <head>, и роботы их не прочтут.
Next.js (предпочтительный способ)
Next.js имеет встроенный компонент <Head>, аналогичный react-helmet, но нативный и не требующий дополнительных библиотек. Пример:
import Head from 'next/head';
export default function Post({ post }) {
return (
<div>
<Head>
<title>{post.title}</title>
<meta name="description" content={post.description} />
<meta property="og:title" content={post.title} />
<meta property="og:description" content={post.description} />
<meta property="og:image" content={post.image} />
</Head>
<h1>{post.title}</h1>
<p>{post.content}</p>
</div>
);
}
Что-то ещё?
Мы рассмотрели основные способы SEO-оптимизации SPA. Теперь пробежимся по другим методам, которые не имеют значительных отличий (кроме архитектурных).
robots.txt
Файл robots.txt указывает поисковым роботам, какие страницы можно сканировать, а какие нет. Удобно для скрытия админ-панели или страниц в разработке.
Пример:
User-agent: *
Allow: /
Disallow: /admin
Sitemap: https://abaunda.org/sitemap.xml
User-agent: *— правила для всех роботов.AllowиDisallow— разрешённые и запрещённые для обхода адреса.Sitemap— ссылка на карту сайта.
Поместите файл в public/ или static/ для прямого доступа по https://abaunda.org/robots.txt.
sitemap.xml
Файл sitemap.xml содержит список всех страниц для индексации. Для React/Next.js рекомендуется библиотека next-sitemap.
После установки в next-sitemap.config.js:
module.exports = {
siteUrl: 'https://abaunda.org',
// generateRobotsTxt: true, // Генерация robots.txt
};
В package.json добавьте:
"scripts": {
"postbuild": "next-sitemap"
}
После сборки проекта будет создан sitemap.xml.
Семантическая вёрстка и атрибуты доступности aria
Не забывайте о семантической вёрстке и атрибутах aria для повышения доступности и SEO.