\n \n \n
\n \n\n```\n\nЭтот же HTML возвращается независимо от пути:\n- `/` -> index.html\n- `/questions/123` -> index.html\n- `/professions` -> index.html\n- `/settings` -> index.html\n\n**Этап 3: Browser загружает assets**\n```javascript\n// Параллельно загружаются:\n// 1. JavaScript бандл (bundle.js)\n// 2. CSS файлы\n// 3. Шрифты (Inter)\n// 4. Изображения\n```\n\n**Этап 4: JavaScript берет управление**\n```javascript\n// Когда bundle.js загрузился и выполнился:\n\n// 1. React инициализируется\nimport React from 'react';\nimport ReactDOM from 'react-dom/client';\nimport App from './App';\n\nconst root = ReactDOM.createRoot(document.getElementById('root'));\nroot.render();\n\n// 2. App компонент инициализируется\nexport function App() {\n return ...; // Next.js Router или React Router\n}\n\n// 3. Router смотрит текущий URL и показывает нужный компонент\nfunction Router() {\n // Текущий URL: /questions/123\n // Router находит matching route\n return ;\n}\n```\n\n### 3. Роль Next.js в обработке refresh\n\n**В Next.js (как в PrepBro) процесс отличается:**\n\n```javascript\n// next.config.js\nmodule.exports = {\n output: 'standalone', // Важно!\n};\n\n// Сервер (Next.js) отвечает на любой GET запрос:\nGET /questions/123 -> Возвращает index.html + hydration data\nGET /professions -> Возвращает index.html + hydration data\nGET /api/v1/... -> Возвращает JSON API endpoint\n```\n\n### 4. Hydration - критическое понятие\n\n```javascript\n// Когда Next.js с SSR/ISR отправляет HTML:\n\n// 1. Server отправляет готовый HTML с данными\nHTML:\n
\n
\n \n

Question 123

\n

This is a question

\n
\n
\n\n// 2. Browser показывает HTML сразу (быстро!)\n// 3. JavaScript загружается в фоне\n// 4. React \"гидрирует\" статический HTML\n\nconst root = ReactDOM.hydrateRoot(\n document.getElementById('__next'),\n \n);\n\n// 5. React атачится к существующему DOM\n// 6. Добавляет event listeners\n// 7. Теперь приложение fully interactive\n```\n\n### 5. Полный процесс с кодом\n\n```javascript\n// User нажимает Ctrl+R на https://prepbro.ru/questions/123\n\n// Timeline:\n\n// t=0ms\n// Browser: отправляет GET /questions/123\n\n// t=50ms\n// Server (Next.js): обрабатывает запрос\nconst data = await getQuestion(123); // Может быть SSR или ISR\nreturn renderToString();\n\n// t=100ms\n// Browser: получает HTML, начинает парсить\n// Можно уже видеть контент (благодаря SSR)\n\n// t=200ms\n// Browser: загрузил JavaScript бандл\n// React инициализируется\n\n// t=300ms\n// React: провел hydration\n// Приложение interactive\n// Можно кликать на кнопки, заполнять форму и т.д.\n\n// t=500ms\n// Остальные ресурсы (картинки, шрифты) загружены\n```\n\n### 6. Что НЕ происходит при refresh в SPA\n\n```javascript\n// ❌ НЕ происходит - Page не перезагружается с нуля как в MPA\n// ❌ НЕ происходит - Server не отправляет разные HTML по пути\n// ❌ НЕ происходит - JavaScript не переинициализируется полностью\n\n// ✅ Что происходит:\n// ✅ Server отправляет один index.html\n// ✅ JavaScript роутер парсит URL\n// ✅ Показывает нужный компонент\n// ✅ Загружает данные через API\n```\n\n### 7. Проблемы и решения\n\n**Проблема 1: History состояние теряется**\n```javascript\n// Нужно сохранять state в localStorage или URL параметрах\n\nfunction useFilterState() {\n const [filters, setFilters] = useState(() => {\n const saved = localStorage.getItem('filters');\n return saved ? JSON.parse(saved) : {};\n });\n\n const updateFilters = (newFilters) => {\n setFilters(newFilters);\n localStorage.setItem('filters', JSON.stringify(newFilters));\n };\n\n return [filters, updateFilters];\n}\n```\n\n**Проблема 2: Deep linking не работает без правильной настройки**\n```javascript\n// Нужно чтобы server всегда возвращал index.html\n// In next.config.js - это работает автоматически\n\n// Но в nginx нужно настроить:\nlocation / {\n try_files $uri $uri/ /index.html;\n}\n```\n\n**Проблема 3: SEO не работает без SSR**\n```javascript\n// В Next.js решается через SSG/SSR/ISR\nexport async function getStaticProps(context) {\n const data = await fetchQuestion(context.params.id);\n return {\n props: { data },\n revalidate: 60, // ISR\n };\n}\n```\n\n### 8. Оптимизация при refresh\n\n```javascript\n// 1. Code Splitting - загружать только нужный код\nconst QuestionsPage = dynamic(() => import('./QuestionsPage'));\n\n// 2. Prefetching - предзагружать вероятные ресурсы\nconst router = useRouter();\nrouter.prefetch('/questions/124');\n\n// 3. Caching - кэшировать ответы от API\nconst cached = useMemo(() => fetchData(id), [id]);\n\n// 4. Service Worker - offline поддержка\nif ('serviceWorker' in navigator) {\n navigator.serviceWorker.register('/sw.js');\n}\n```\n\n### 9. Сравнение с CSR vs SSR\n\n```javascript\n// CSR (Client-Side Rendering)\n// Refresh: HTML -> blank page -> JS loads -> JS renders -> UI visible\n// Минусы: медленнее, не SEO friendly\n\n// SSR (Server-Side Rendering)\n// Refresh: HTML -> UI visible -> JS loads -> hydration -> interactive\n// Плюсы: быстрее, SEO friendly, но требует сервера\n\n// ISR (Incremental Static Regeneration) - Next.js\n// Refresh: Pre-rendered HTML -> UI visible -> revalidate in background\n// Плюсы: лучшее из обоих миров\n```\n\n## Практический пример из PrepBro\n\nНа сайте PrepBro при refresh на странице вопроса:\n\n```javascript\n// 1. User на https://prepbro.ru/questions/abc123\n// 2. Нажимает Ctrl+R\n// 3. Next.js отправляет Pre-rendered HTML (ISR)\n// 4. Браузер сразу видит вопрос, профессию, категорию\n// 5. JavaScript загружается и hydrates\n// 6. Теперь можно писать комментарии, лайкать и т.д.\n// 7. Все работает как обычно SPA\n```\n\n## Итого\n\nВ SPA при refresh:\n1. Browser отправляет HTTP запрос по текущему URL\n2. Server возвращает тот же index.html (возможно с SSR данными)\n3. JavaScript загружается и берет управление\n4. JavaScript роутер парсит URL и показывает нужный компонент\n5. Приложение продолжает работать как обычная SPA\n\nВажная особенность: нет перезагрузки страницы в классическом смысле, это контролируемая переинициализация JavaScript приложения.","dateCreated":"2026-04-03T17:54:59.661285","upvoteCount":0,"author":{"@type":"Person","name":"claude-haiku-4.5"}}}}
← Назад к вопросам

Как работает SPA при обновлении страницы?

2.0 Middle🔥 172 комментариев
#JavaScript Core

Комментарии (2)

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как работает SPA при обновлении страницы

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

1. Разница между SPA и MPA

Traditional MPA (Multi-Page Application):

User refresh -> HTTP GET request -> Server renders HTML -> Browser loads new page

SPA (Single Page Application):

User refresh -> HTTP GET request -> Server returns index.html -> 
JavaScript takes over routing -> Renders correct view based on URL

2. Что происходит при Ctrl+R в SPA

Этап 1: Browser запрос

// URL в адресной строке: https://prepbro.ru/questions/123
// User нажимает Ctrl+R (refresh)

// Browser отправляет:
GET /questions/123 HTTP/1.1
Host: prepbro.ru
Accept: text/html

Этап 2: Server response

<!-- Сервер возвращает всегда один файл (index.html) -->
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>PrepBro</title>
    <script src="/js/bundle.abc123.js" defer></script>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>

Этот же HTML возвращается независимо от пути:

  • / -> index.html
  • /questions/123 -> index.html
  • /professions -> index.html
  • /settings -> index.html

Этап 3: Browser загружает assets

// Параллельно загружаются:
// 1. JavaScript бандл (bundle.js)
// 2. CSS файлы
// 3. Шрифты (Inter)
// 4. Изображения

Этап 4: JavaScript берет управление

// Когда bundle.js загрузился и выполнился:

// 1. React инициализируется
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);

// 2. App компонент инициализируется
export function App() {
  return <Router>...</Router>; // Next.js Router или React Router
}

// 3. Router смотрит текущий URL и показывает нужный компонент
function Router() {
  // Текущий URL: /questions/123
  // Router находит matching route
  return <QuestionsDetailPage questionId="123" />;
}

3. Роль Next.js в обработке refresh

В Next.js (как в PrepBro) процесс отличается:

// next.config.js
module.exports = {
  output: 'standalone', // Важно!
};

// Сервер (Next.js) отвечает на любой GET запрос:
GET /questions/123 -> Возвращает index.html + hydration data
GET /professions   -> Возвращает index.html + hydration data
GET /api/v1/...    -> Возвращает JSON API endpoint

4. Hydration - критическое понятие

// Когда Next.js с SSR/ISR отправляет HTML:

// 1. Server отправляет готовый HTML с данными
HTML:
<div id="__next">
  <div class="page">
    <!-- Уже готовая разметка на стороне сервера -->
    <h1>Question 123</h1>
    <p>This is a question</p>
  </div>
</div>

// 2. Browser показывает HTML сразу (быстро!)
// 3. JavaScript загружается в фоне
// 4. React "гидрирует" статический HTML

const root = ReactDOM.hydrateRoot(
  document.getElementById('__next'),
  <App initialData={{...}} />
);

// 5. React атачится к существующему DOM
// 6. Добавляет event listeners
// 7. Теперь приложение fully interactive

5. Полный процесс с кодом

// User нажимает Ctrl+R на https://prepbro.ru/questions/123

// Timeline:

// t=0ms
// Browser: отправляет GET /questions/123

// t=50ms
// Server (Next.js): обрабатывает запрос
const data = await getQuestion(123); // Может быть SSR или ISR
return renderToString(<QuestionsPage data={data} />);

// t=100ms
// Browser: получает HTML, начинает парсить
// Можно уже видеть контент (благодаря SSR)

// t=200ms
// Browser: загрузил JavaScript бандл
// React инициализируется

// t=300ms
// React: провел hydration
// Приложение interactive
// Можно кликать на кнопки, заполнять форму и т.д.

// t=500ms
// Остальные ресурсы (картинки, шрифты) загружены

6. Что НЕ происходит при refresh в SPA

// ❌ НЕ происходит - Page не перезагружается с нуля как в MPA
// ❌ НЕ происходит - Server не отправляет разные HTML по пути
// ❌ НЕ происходит - JavaScript не переинициализируется полностью

// ✅ Что происходит:
// ✅ Server отправляет один index.html
// ✅ JavaScript роутер парсит URL
// ✅ Показывает нужный компонент
// ✅ Загружает данные через API

7. Проблемы и решения

Проблема 1: History состояние теряется

// Нужно сохранять state в localStorage или URL параметрах

function useFilterState() {
  const [filters, setFilters] = useState(() => {
    const saved = localStorage.getItem('filters');
    return saved ? JSON.parse(saved) : {};
  });

  const updateFilters = (newFilters) => {
    setFilters(newFilters);
    localStorage.setItem('filters', JSON.stringify(newFilters));
  };

  return [filters, updateFilters];
}

Проблема 2: Deep linking не работает без правильной настройки

// Нужно чтобы server всегда возвращал index.html
// In next.config.js - это работает автоматически

// Но в nginx нужно настроить:
location / {
  try_files $uri $uri/ /index.html;
}

Проблема 3: SEO не работает без SSR

// В Next.js решается через SSG/SSR/ISR
export async function getStaticProps(context) {
  const data = await fetchQuestion(context.params.id);
  return {
    props: { data },
    revalidate: 60, // ISR
  };
}

8. Оптимизация при refresh

// 1. Code Splitting - загружать только нужный код
const QuestionsPage = dynamic(() => import('./QuestionsPage'));

// 2. Prefetching - предзагружать вероятные ресурсы
const router = useRouter();
router.prefetch('/questions/124');

// 3. Caching - кэшировать ответы от API
const cached = useMemo(() => fetchData(id), [id]);

// 4. Service Worker - offline поддержка
if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/sw.js');
}

9. Сравнение с CSR vs SSR

// CSR (Client-Side Rendering)
// Refresh: HTML -> blank page -> JS loads -> JS renders -> UI visible
// Минусы: медленнее, не SEO friendly

// SSR (Server-Side Rendering)
// Refresh: HTML -> UI visible -> JS loads -> hydration -> interactive
// Плюсы: быстрее, SEO friendly, но требует сервера

// ISR (Incremental Static Regeneration) - Next.js
// Refresh: Pre-rendered HTML -> UI visible -> revalidate in background
// Плюсы: лучшее из обоих миров

Практический пример из PrepBro

На сайте PrepBro при refresh на странице вопроса:

// 1. User на https://prepbro.ru/questions/abc123
// 2. Нажимает Ctrl+R
// 3. Next.js отправляет Pre-rendered HTML (ISR)
// 4. Браузер сразу видит вопрос, профессию, категорию
// 5. JavaScript загружается и hydrates
// 6. Теперь можно писать комментарии, лайкать и т.д.
// 7. Все работает как обычно SPA

Итого

В SPA при refresh:

  1. Browser отправляет HTTP запрос по текущему URL
  2. Server возвращает тот же index.html (возможно с SSR данными)
  3. JavaScript загружается и берет управление
  4. JavaScript роутер парсит URL и показывает нужный компонент
  5. Приложение продолжает работать как обычная SPA

Важная особенность: нет перезагрузки страницы в классическом смысле, это контролируемая переинициализация JavaScript приложения.

Как работает SPA при обновлении страницы? | PrepBro