Урок 47 из 49 · Месяц 6. TypeScript и выход на работу

Основы SEO-оптимизации

Содержание урока

Можно сделать отличный сайт, но если его не найти в поиске, о нём никто не узнает. SEO — это набор правил, которые помогают поисковикам понять ваш сайт и показать его людям выше в результатах. В этом уроке разберём, как работают поисковые системы, какие мета-теги и семантику нужно добавить, почему у одностраничных React-приложений проблемы с SEO и как их решают SSR и Next.js, что такое Core Web Vitals, robots.txt и sitemap.

Что такое SEO и как работает поиск

SEO (Search Engine Optimization) — «оптимизация для поисковых систем». Её задача — чтобы Google, Яндекс и другие поисковики правильно поняли страницу и показали её по нужным запросам.

Поисковик работает в три шага:

  1. Сканирование (crawling). Программа-робот (краулер) ходит по ссылкам и скачивает страницы.
  2. Индексация (indexing). Поисковик разбирает текст, заголовки, ссылки и сохраняет страницу в свою базу — индекс.
  3. Ранжирование (ranking). Когда человек что-то ищет, поисковик выбирает из индекса подходящие страницы и сортирует их.

Аналогия: поисковик — библиотекарь. Сначала он собирает книги (сканирование), потом составляет карточки в каталоге (индексация), а когда читатель спрашивает «что почитать про React?», достаёт самые подходящие (ранжирование). Наша задача — чтобы книгу было легко найти и на обложке было понятно, о чём она.

Мета-теги

Мета-теги лежат в <head> и пользователь их не видит, а вот поисковики и соцсети читают внимательно.

<!doctype html>
<html lang="ru">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />

    <title>Беспроводные наушники — купить в Бишкеке | Shop</title>
    <meta
      name="description"
      content="Беспроводные наушники с шумоподавлением. Доставка по городу за 1 день, гарантия 12 месяцев."
    />
    <link rel="canonical" href="https://shop.example.com/catalog/headphones" />

    <meta property="og:title" content="Беспроводные наушники | Shop" />
    <meta property="og:description" content="Шумоподавление, 30 часов работы." />
    <meta property="og:image" content="https://shop.example.com/og/headphones.jpg" />
    <meta property="og:url" content="https://shop.example.com/catalog/headphones" />
    <meta property="og:type" content="website" />
  </head>
  <body>
    ...
  </body>
</html>

Разбор по строкам:

  • lang="ru" — язык страницы; помогает поисковику и экранным дикторам;
  • viewport — обязательный тег для мобильных; без него сайт считается неадаптивным;
  • <title> — самый важный тег: заголовок вкладки и синяя ссылка в выдаче. Около 50–60 символов, главное — в начале, уникальный для каждой страницы;
  • description — описание под ссылкой в выдаче (поисковик может заменить его своим). Около 140–160 символов, живым языком;
  • canonical — «главный» адрес страницы. Нужен, если одна страница открывается по нескольким адресам (?sort=price, ?utm=...), чтобы поисковик не считал их дублями;
  • og:* — протокол Open Graph: так выглядит превью ссылки в Telegram, WhatsApp, Facebook.

Ещё один полезный тег — запрет индексации для служебных страниц (личный кабинет, корзина, результаты поиска по сайту):

<meta name="robots" content="noindex, nofollow" />

Семантическая вёрстка

Поисковый робот не видит дизайн — он читает HTML. Семантические теги (урок HTML5: основные теги) объясняют ему структуру страницы.

<body>
  <header>
    <a href="/">Shop</a>
    <nav>
      <a href="/catalog">Каталог</a>
      <a href="/delivery">Доставка</a>
    </nav>
  </header>

  <main>
    <article>
      <h1>Беспроводные наушники X200</h1>
      <img src="/img/x200.webp" alt="Чёрные беспроводные наушники X200 в кейсе" width="600" height="600" />

      <section>
        <h2>Характеристики</h2>
        <ul>
          <li>Шумоподавление</li>
          <li>30 часов работы</li>
        </ul>
      </section>

      <section>
        <h2>Отзывы</h2>
        ...
      </section>
    </article>
  </main>

  <footer>© 2026 Shop</footer>
</body>

Правила:

  • один <h1> на страницу — главная тема; дальше <h2>, <h3> по порядку, без пропусков уровней;
  • <header>, <nav>, <main>, <article>, <section>, <footer> вместо безликих <div>;
  • у каждой картинки осмысленный alt — поисковик не видит изображения, он читает их описание;
  • ссылки — это <a href>, а не <div onClick>: робот переходит только по настоящим ссылкам;
  • текст ссылки говорит, куда она ведёт: «Условия доставки», а не «нажмите здесь».

Бонус: семантика одновременно улучшает доступность — экранные дикторы для незрячих людей читают ту же структуру.

SPA и SEO

В чём проблема

React-приложение на Vite — это SPA (Single Page Application, урок SPA на React Router). Посмотрите, какой HTML приходит с сервера (Ctrl+U — «Просмотр кода страницы»):

<body>
  <div id="root"></div>
  <script type="module" src="/assets/index-a1b2c3.js"></script>
</body>

Внутри пусто. Весь контент появляется только после того, как браузер скачает и выполнит JavaScript. Это называется CSR (Client-Side Rendering — отрисовка на стороне клиента).

Что из этого следует:

  • Google умеет выполнять JavaScript, но делает это позже и не всегда полностью — индексация медленнее и менее надёжна;
  • многие другие роботы (превью в мессенджерах, часть поисковиков) JavaScript не выполняют и видят пустую страницу;
  • у всех страниц одинаковый <title> из index.html, если не менять его вручную;
  • пользователь дольше смотрит на белый экран — это ухудшает метрики скорости.

Мета-теги в React 19

Начиная с React 19, теги <title>, <meta> и <link> можно писать прямо внутри компонента — React сам перенесёт их в <head>:

type Product = { id: number; title: string; description: string };

export function ProductPage({ product }: { product: Product }) {
  return (
    <article>
      <title>{`${product.title} — купить | Shop`}</title>
      <meta name="description" content={product.description} />
      <link rel="canonical" href={`https://shop.example.com/products/${product.id}`} />

      <h1>{product.title}</h1>
      <p>{product.description}</p>
    </article>
  );
}

Обратите внимание: <title> получает одну строку через шаблон `...`, а не смесь текста и выражений — так React корректно обновит заголовок.

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

SSR, SSG и Next.js

Решение — отдавать с сервера уже готовый HTML. Есть несколько способов:

Подход Когда создаётся HTML Подходит для
CSR В браузере, после загрузки JS Личные кабинеты, админки — где SEO не нужно
SSR (Server-Side Rendering) На сервере при каждом запросе Каталоги, новости — данные часто меняются
SSG (Static Site Generation) Один раз при сборке проекта Лендинги, блоги, документация
ISR (Incremental Static Regeneration) При сборке, и обновляется по таймеру Каталог, который меняется раз в час

Аналогия: CSR — вам привозят набор «собери сам» (мебель из коробки), SSR — привозят собранный шкаф под заказ, SSG — шкаф со склада, собранный заранее.

Самый популярный фреймворк для этого в мире React — Next.js. В нём страница — это React-компонент, который по умолчанию отрисовывается на сервере, а мета-теги задаются специальным экспортом. Пример в стиле App Router:

// app/products/[id]/page.tsx
import type { Metadata } from "next";

type Props = { params: Promise<{ id: string }> };

async function getProduct(id: string) {
  const res = await fetch(`https://api.example.com/products/${id}`);
  return res.json() as Promise<{ title: string; description: string }>;
}

export async function generateMetadata({ params }: Props): Promise<Metadata> {
  const { id } = await params;
  const product = await getProduct(id);
  return {
    title: `${product.title} | Shop`,
    description: product.description,
  };
}

export default async function ProductPage({ params }: Props) {
  const { id } = await params;
  const product = await getProduct(id);

  return (
    <main>
      <h1>{product.title}</h1>
      <p>{product.description}</p>
    </main>
  );
}

Что здесь важно на уровне обзора:

  • компонент страницы — async, он получает данные на сервере, и браузер сразу получает HTML с заголовком и текстом;
  • generateMetadata формирует <title> и description для каждой страницы отдельно;
  • папка [id] — динамический сегмент адреса, как :id в React Router.

Подробно Next.js изучают отдельно — документация на nextjs.org/docs. Сейчас достаточно понимать, зачем он нужен и чем SSR отличается от SPA.

Core Web Vitals: скорость и удобство

Поисковики учитывают, насколько удобно пользоваться страницей. Google измеряет это тремя метриками — Core Web Vitals:

Метрика Что измеряет Хорошо
LCP (Largest Contentful Paint) Когда появился самый крупный элемент (главная картинка, заголовок) до 2,5 с
INP (Interaction to Next Paint) Как быстро страница реагирует на клики и ввод до 200 мс
CLS (Cumulative Layout Shift) Насколько «прыгает» вёрстка во время загрузки до 0,1

Как улучшать:

  • LCP: сжимайте картинки (WebP/AVIF), не грузите огромные баннеры, главной картинке добавьте fetchpriority="high";
  • INP: не делайте тяжёлых вычислений при каждом клике, разбивайте большие компоненты, используйте useMemo по делу (урок useRef, useMemo и useCallback);
  • CLS: указывайте width и height у картинок, резервируйте место под баннеры и шрифты.
<!-- главная картинка: грузим сразу и с высоким приоритетом -->
<img src="/hero.webp" alt="Новая коллекция" width="1200" height="600" fetchpriority="high" />

<!-- картинки ниже первого экрана: грузим, только когда пользователь до них долистает -->
<img src="/card-1.webp" alt="Кроссовки" width="400" height="400" loading="lazy" />

Проверить метрики можно во вкладке Lighthouse в Chrome DevTools: «Analyze page load» — и через пару секунд получите оценки Performance, Accessibility, Best Practices и SEO с советами, что исправить.

robots.txt и sitemap.xml

robots.txt

robots.txt — текстовый файл в корне сайта (https://site.com/robots.txt) с правилами для роботов: куда можно, куда нельзя.

User-agent: *
Disallow: /admin/
Disallow: /cart
Disallow: /profile

Sitemap: https://shop.example.com/sitemap.xml
  • User-agent: * — правила для всех роботов;
  • Disallow — разделы, которые не нужно сканировать;
  • Sitemap — где лежит карта сайта.

sitemap.xml

Карта сайта — список всех страниц, которые вы хотите видеть в поиске:

<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>https://shop.example.com/</loc>
    <lastmod>2026-09-01</lastmod>
  </url>
  <url>
    <loc>https://shop.example.com/catalog</loc>
    <lastmod>2026-09-20</lastmod>
  </url>
  <url>
    <loc>https://shop.example.com/catalog/headphones</loc>
    <lastmod>2026-09-25</lastmod>
  </url>
</urlset>

В проекте на Vite оба файла кладут в папку public/ — при сборке они окажутся в корне сайта. В больших проектах sitemap генерируют автоматически (в Next.js для этого есть файлы app/sitemap.ts и app/robots.ts).

После публикации сайт добавляют в Google Search Console и Яндекс Вебмастер — там видно, какие страницы проиндексированы и какие ошибки нашёл робот.

Типичные ошибки

  • Одинаковые title и description на всех страницах.
  • Несколько <h1> или заголовки ради крупного шрифта, а не структуры.
  • Картинки без alt и без width/height — минус к SEO и прыгающая вёрстка.
  • Навигация на <div onClick> вместо <a href> — робот не найдёт страницы.
  • Ожидание, что SPA на Vite будет хорошо индексироваться «само».
  • Случайно оставленный noindex или Disallow: / после тестового сервера — сайт пропадает из поиска.

Практика

  1. Мета-теги. Добавьте в index.html своего проекта lang, title, description, Open Graph. Ожидаемый результат: ссылка, отправленная в мессенджер после деплоя, показывает превью с картинкой и описанием.
  2. Семантика. Переделайте одну страницу проекта: один <h1>, <main>, <nav>, alt у всех картинок. Ожидаемый результат: в Lighthouse оценка SEO и Accessibility выросла.
  3. Заголовки в SPA. Используя теги <title> и <meta> из React 19, сделайте уникальный заголовок вкладки на каждой странице вашего React Router-приложения. Ожидаемый результат: при переходах меняется название вкладки.
  4. robots и sitemap. Создайте public/robots.txt и public/sitemap.xml для своего проекта. Ожидаемый результат: после npm run build файлы лежат в dist/ и открываются по /robots.txt и /sitemap.xml.
  5. Аудит скорости. Запустите Lighthouse, выпишите LCP и CLS, исправьте две проблемы (сжать картинки, добавить размеры, loading="lazy"). Ожидаемый результат: оценка Performance выросла, CLS не больше 0,1.

Итоги

  • Поисковик сканирует, индексирует и ранжирует страницы; фронтендер отвечает за техническое SEO.
  • Главные теги: title, description, viewport, canonical, Open Graph; служебные страницы закрываем noindex.
  • Семантика: один <h1>, иерархия заголовков, <main>/<nav>/<article>, alt и настоящие ссылки.
  • SPA отдаёт пустой HTML — для публичных страниц используют SSR/SSG, например Next.js.
  • Core Web Vitals: LCP до 2,5 с, INP до 200 мс, CLS до 0,1; проверяем в Lighthouse.
  • robots.txt подсказывает роботам, куда не ходить, sitemap.xml — какие страницы есть; в Vite оба файла лежат в public/.
Отзыв