close

Генерация статического сайта (SSG)

Что такое SSG

SSG (Static Site Generation) — это способ предварительной генерации страниц в виде HTML-файлов на этапе сборки, а не во время обращения пользователя к сайту.

Преимущества SSG:

  • Быстрее отображается первый контент: пользователю не нужно ждать загрузки и выполнения JavaScript — страница полностью видна сразу после загрузки HTML-файла браузером
  • Дружелюбно к SEO: поисковые роботы могут напрямую получить готовый HTML-контент
  • Простое развёртывание: результат сборки — чистые статические файлы, для которых не нужен сервер; их можно разместить на любом статическом хостинге

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

Различия между dev и build

Rspress использует разные стратегии рендеринга в зависимости от режима: в процессе разработки применяется Client-Side Rendering (CSR) для более комфортной работы, а в продакшен-сборке по умолчанию используется SSG для достижения оптимальной производительности.

АспектРежим разработкиРежим сборки (Продакшен)
Командаrspress devrspress build
РендерингЧистый CSR (Client-Side Rendering)SSG (по умолчанию) или CSR
Предварительный рендерингОтсутствуетПредварительно рендерит все страницы при включённом SSG
Основная цельОтладка, горячая перезагрузка (HMR)Производительность, SEO
Способ просмотраПрямой доступ к dev-серверуrspress preview

Режим разработки

rspress dev

В режиме разработки используется чистый рендеринг на стороне клиента (CSR) без предварительного рендеринга. Это позволяет максимально ускорить итерации и обеспечить полноценную работу Hot Module Replacement (HMR — горячая перезагрузка модулей).

Совет

Если ваш код отлично работает в режиме разработки, но выдаёт ошибки при сборке, чаще всего причина в том, что SSG выполняет рендеринг в среде Node.js и не имеет доступа к браузерным API (таким как window или document). Подробные решения смотрите в разделе «Распространённые проблемы и их решения» ниже.

Режим сборки

rspress build

Режим сборки включает SSG по умолчанию. Вы можете управлять этим поведением через конфигурацию ssg:

  • ssg: true (по умолчанию): включает SSG. Во время сборки Rspress выполняет рендеринг React-компонентов в среде Node.js, преобразуя каждую страницу в полноценный HTML-файл с готовым содержимым.
  • ssg: false: отключает SSG. Используется чистый CSR. Сгенерированный HTML содержит только пустой контейнер, который ожидает рендеринга на стороне клиента.

После выполнения сборки:

  • Локальный просмотр: используйте команду rspress preview, чтобы запустить локальный статический сервер и просмотреть результат сборки.

    rspress preview
  • Развёртывание на сервере: Разместите содержимое директории doc_build на любом сервисе статического хостинга (GitHub Pages, Netlify, Vercel и т. д.)

SSG vs CSR — результат сборки

Структура выходной директории

Независимо от того, используется режим SSG или CSR, структура выходной директории остаётся одинаковой:

doc_build/
├── static/
│   ├── js/
│   │   ├── main.[hash].js
│   │   └── async/
│   └── css/
│       └── main.[hash].css
├── index.html
├── 404.html
├── guide/
│   └── getting-started.html
└── api/
    └── config.html

Файл 404.html автоматически генерируется Rspress для обработки несуществующих маршрутов. Этот файл играет важную роль при развёртывании в формате SPA, подробности см. в разделе «Страница показывает 404 после обновления».

Различия в содержимом HTML

Основное различие между двумя режимами заключается в содержимом генерируемых HTML-файлов:

HTML в режиме SSG (полностью предрендеренное содержимое):

<body>
  <div id="__rspress_root">
    <!-- полностью предрендеренный контент страницы -->
    <nav>...</nav>
    <main>
      <article>
        <h1>Первые шаги</h1>
        <p>Добро пожаловать в Rspress...</p>
      </article>
    </main>
  </div>
  <script src="/static/js/main.[hash].js"></script>
</body>

HTML в режиме CSR (только пустой контейнер, ожидает рендеринга JavaScript):

<body>
  <div id="__rspress_root"></div>
  <script src="/static/js/main.[hash].js"></script>
</body>

Различия в процессе загрузки

Процесс загрузки в режиме SSG:

  1. Браузер загружает HTML → Пользователь сразу видит полностью готовый контент
  2. JavaScript завершает загрузку → React выполняет гидратацию, привязывает обработчики событий и интерактивность
  3. Последующая навигация → Режим SPA, рендеринг на стороне клиента

Процесс загрузки в режиме CSR:

  1. Браузер загружает HTML → Пользователь видит пустую страницу
  2. JavaScript завершает загрузку → React рендерит содержимое страницы
  3. Последующая навигация → Режим SPA, рендеринг на стороне клиента

Распространённые проблемы и их решения

window is not defined / document is not defined

Причина: SSG выполняет рендеринг страниц в среде Node.js, где отсутствуют глобальные объекты браузера, такие как window и document.

Решения:

  1. Используйте useEffect для отложенного выполнения: Помещайте вызовы браузерных API внутрь useEffect, чтобы они выполнялись только на стороне клиента

    import { useEffect, useState } from 'react';
    
    function MyComponent() {
      const [width, setWidth] = useState(0);
    
      useEffect(() => {
        // Запускается только на клиенте
        setWidth(window.innerWidth);
      }, []);
    
      return <div>Ширина окна: {width}</div>;
    }
  2. Проверка окружения: Проверяйте среду перед обращением к браузерным API

    if (typeof window !== 'undefined') {
      // Среда браузера
      console.log(window.location.href);
    }
  3. Динамический импорт: Для сторонних библиотек, которые зависят от браузерных API, используйте динамический импорт

    import { useEffect, useState } from 'react';
    
    function MyComponent() {
      const [Editor, setEditor] = useState(null);
    
      useEffect(() => {
        import('some-browser-only-library').then((mod) => {
          setEditor(() => mod.default);
        });
      }, []);
    
      if (!Editor) return <div>Загрузка...</div>;
      return <Editor />;
    }

Несоответствие при гидратации

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

Типичные ситуации, приводящие к проблеме:

  • Использование Date.now() или случайных чисел (Math.random())
  • Рендеринг разного контента в зависимости от свойств объекта window (например, window.innerWidth, window.matchMedia)
  • Использование данных, доступных только на клиенте (например, localStorage, sessionStorage)

Решение: Убедитесь, что результат первого рендера полностью одинаков на сервере и на клиенте. Для любого контента, который должен динамически изменяться на стороне клиента, используйте useEffect — обновляйте состояние уже после завершения гидратации.

import { useEffect, useState } from 'react';

function MyComponent() {
  // Первый рендер использует значение по умолчанию для согласованности между сервером и клиентом
  const [theme, setTheme] = useState('light');

  useEffect(() => {
    // Читаем значение из localStorage уже после завершения гидратации
    const savedTheme = localStorage.getItem('theme');
    if (savedTheme) {
      setTheme(savedTheme);
    }
  }, []);

  return <div className={theme}>...</div>;
}

Страница показывает 404 после обновления

Симптом: Навигация по другим страницам внутри сайта работает нормально, но при обновлении страницы появляется ошибка 404.

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

Решение: Большинство сервисов статического хостинга (GitHub Pages, Netlify, Vercel и др.) по умолчанию используют файл 404.html для обработки всех несовпадающих маршрутов — в этом случае дополнительная настройка не требуется. Если ваш сервер не обрабатывает это автоматически, необходимо вручную настроить перенаправление всех несуществующих запросов на файл 404.html. Файл 404.html, сгенерированный Rspress, содержит полный код приложения, поэтому он корректно обрабатывает маршрутизацию на стороне клиента и отображает нужную страницу.

Вот пример файла _redirects для настройки перенаправления на 404.html (используйте его, если ваш хостинг поддерживает такой формат):

docs/public/_redirects
/*    /404.html   200

Конфигурация

Вы можете управлять включением SSG с помощью параметра конфигурации ssg:

rspress.config.ts
import { defineConfig } from '@rspress/core';

export default defineConfig({
  ssg: true, // значение по умолчанию, SSG включен
});

Если ваш сайт имеет особые требования, вы можете отключить SSG:

rspress.config.ts
import { defineConfig } from '@rspress/core';

export default defineConfig({
  ssg: false, // Отключаем SSG, используем CSR
});
Предупреждение

Будьте осторожны при отключении SSG — вы потеряете преимущества быстрого отображения первого контента (First Contentful Paint) и улучшенной индексации поисковыми системами (SEO).

Пользовательский HTML-контент

Если вам нужно внедрить пользовательские теги в HTML (например, meta-теги, код аналитики, скрипты или стили), обратитесь к Настройке тегов Head.