Ось готовий урок, створений спеціально для тебе у стилі David Malan. Вмикай уяву — ми в лекційній залі Sanders Theatre!
🎓 УРОК: Serving SPA (React, Angular, Vue)
Або: Чому мій сайт працює, поки я не натисну F5?
1. 🔥 Вступ: «Магія зникає, коли оновлюєш сторінку»
Уявіть ситуацію. Ви витратили тижні на створення ідеального інтернет-магазину на React. Все літає! Клієнт натискає «Каталог», потім «Товар», потім «Кошик». URL у браузері змінюється: /catalog -> /product/42 -> /cart. Ви задоволені, відчуваєте себе хакером.
Ви заливаєте це на сервер, відправляєте посилання другу: myshop.com/product/42.
Друг відкриває посилання і... бачить помилку 404 Not Found.
Або ще гірше: ви самі на сайті, знаходитесь на сторінці /cart, випадково натискаєте кнопку «Оновити» (Refresh)... і знову 404.
Чекайте, що? Чому, коли ви клікали по кнопках, сторінка відкривалася, а коли ви ввели ту саму адресу напряму — сервер каже, що її не існує?
Аналогія: Готель «SPA» 🏨
Уявіть, що ваш сайт — це готель.
- Класичний сайт (MPA): Кожна кімната має власні двері з вулиці. Хочете в кімнату
/contact? Ви заходите в окремі двері. Хочете в/about? Інші двері. Сервер — це швейцар, який відкриває конкретні двері. - SPA (Single Page Application): У цього готелю є ТІЛЬКИ ОДИН ВХІД — головний хол (
index.html). Ви заходите в хол, а там стоїть адміністратор (JavaScript), який миттєво змінює декорації навколо вас. Ви кажете: «Хочу в спальню», і адміністратор клацає пальцями — пуф! — хол виглядає як спальня.
Проблема: Коли ви вводите myshop.com/cart у браузері і тиснете Enter, ви, по суті, намагаєтесь залізти у вікно кімнати «Cart» з вулиці. Але в будівлі SPA немає вікон і дверей для кімнат! Є тільки вхід через хол. Сервер дивиться на вас і каже: «Чувак, кімнати /cart фізично не існує. Є тільки index.html. До побачення (404)».
Сьогодні ми навчимо сервер направляти всіх гостей через головний вхід, незалежно від того, куди вони хочуть потрапити.
2. 🧠 Теоретична база: Що відбувається «під капотом»?
Давайте розберемося з механікою.
Ключові поняття:
-
Client-Side Routing (Маршрутизація на клієнті): Це коли React/Angular змінює URL в адресному рядку, використовуючи
History API, але не робить запит на сервер. Браузер просто «прикидається», що перейшов на нову сторінку. -
Server-Side Routing (Маршрутизація на сервері): Це коли сервер отримує запит, дивиться на шлях (path) і шукає відповідний файл на диску.
У чому конфлікт?
Коли ви робите build свого React-додатку, ви отримуєте одну папку з файлами:
* index.html (єдиний HTML файл)
* bundle.js (весь ваш код)
* style.css
Там немає папки /cart. Там немає файлу /user/profile.
Тому, коли приходить запит GET /cart, сервер (наприклад, Nginx або Apache) чесно шукає файл cart. Не знаходить. Віддає 404.
💡 Рішення (The "Fallback" Strategy)
Ми повинні дати серверу просту інструкцію:
"Якщо хтось просить файл, якого в тебе немає (наприклад,
/cart), не панікуй. Просто віддай йомуindex.html."
Як тільки браузер завантажить index.html та запустить JS, ваш React-код подивиться на URL, зрозуміє: «Ага, тут написано /cart!» — і відрендерить кошик.
3. 🧪 Приклади: Від болю до успіху
Приклад 1: Наївний підхід (Як не треба робити)
Ви просто встановили простий HTTP-сервер і вказали йому на папку build.
- Запит:
GET / - Відповідь сервера:
index.html(200 OK) -> Сайт завантажується, JS працює. - Дія: Клікаємо на лінк "Про нас" -> URL стає
/about. - Дія: Тиснемо F5 (Refresh).
- Запит:
GET /about - Відповідь сервера: 404 Not Found (бо файлу
about.htmlне існує).
Очікування студента: "Ну він же працював секунду тому!"
Реальність: Працював JS у пам'яті браузера. Сервер про /about нічого не знає.
Приклад 2: Nginx (Золотий стандарт)
Nginx — це найпопулярніший веб-сервер для віддачі статики. Ось як виглядає магічна конфігурація.
server {
listen 80;
server_name mysite.com;
root /var/www/my-react-app; # Де лежать ваші файли після build
index index.html;
location / {
# ⚠️ ОСЬ ВОНА, МАГІЯ:
try_files $uri $uri/ /index.html;
}
}
Розбір рядка try_files:
1. $uri — Сервер, подивись, чи існує такий файл (наприклад, logo.png). Якщо є — віддай.
2. $uri/ — Якщо це папка — віддай її.
3. /index.html — Якщо нічого не знайшов — віддай index.html!
Тепер, коли ви запитуєте /cart:
1. Файлу cart немає.
2. Папки cart немає.
3. Nginx віддає index.html.
4. React запускається, бачить /cart і показує кошик. Profit! 🚀
Приклад 3: Node.js + Express (Якщо ви full-stack)
Якщо ви серверите статику через свій Node.js сервер:
const express = require('express');
const path = require('path');
const app = express();
// 1. Спочатку віддаємо статику (картинки, JS, CSS)
app.use(express.static(path.join(__dirname, 'build')));
// 2. Ловимо ВСІ інші запити ('*')
app.get('*', (req, res) => {
// І віддаємо index.html
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
app.listen(3000);
Питання до студента: Чому app.get('*') має йти після app.use(...)?
Відповідь: Бо якщо ми поставимо його першим, то навіть на запит style.css сервер віддасть index.html, і сайт зламається. Порядок має значення!
4. 🛠 Практична частина
А тепер ваша черга. Немає нічого кращого за практику, щоб закріпити нейронні зв'язки.
Завдання 1: "The Build"
Створіть простий React-додаток (create-react-app або Vite). Додайте react-router. Зробіть дві сторінки: Home і About.
Зробіть npm run build. Подивіться, що в папці dist (або build). Знайдіть там файл about? (Спойлер: його там немає).
Завдання 2: "Serve it locally"
Встановіть простий сервер: npm install -g serve.
Запустіть: serve -s build.
Прапор -s означає "single page application". Спробуйте запустити без нього і зловіть 404 помилку при оновленні сторінки. Зрозумійте різницю.
Завдання 3: "Nginx Config doctor" У вас є конфіг, який не працює. Знайдіть помилку:
location / {
try_files $uri $uri/ =404;
}
Підказка: Що станеться, якщо я перейду на /dashboard? Користувач побачить сторінку 404 замість додатку. Виправте це.
Завдання 4: "А що з API?" (Міні-кейс)
Ваш додаток робить запити на /api/users.
Якщо ви використовуєте правило "всі запити на index.html", що станеться з API-запитами, якщо бекенд на тому ж домені?
Завдання: Напишіть (на папері або в коді) логіку, щоб запити, які починаються з /api, йшли на бекенд, а всі інші — на index.html.
5. 💡 Мислення як у розробника
Як відрізнити новачка від профі в цій темі?
1. "It works on my machine" синдром
Новачок запускає npm start і думає, що це і є сервер.
Профі знає: npm start — це важкий dev-server тільки для розробки. У продакшн іде тільки скомпільована статика (HTML/CSS/JS).
2. Кешування (Caching)
Новачок просто віддає файли.
Профі думає: "Я оновив код, але користувачі бачать стару версію". Профі налаштовує заголовки так, щоб index.html ніколи не кешувався (no-cache), а файли з хешами (main.a1b2c3.js) кешувалися назавжди.
3. Використання готових рішень Замість того, щоб налаштовувати Nginx на віртуальній машині (VPS), досвідчений розробник часто скаже: "Навіщо мені цей головний біль?" і заллє проєкт на Vercel, Netlify або AWS S3 + CloudFront. Ці сервіси вже налаштовані під SPA за замовчуванням. Пам'ятайте: лінивий розробник — розумний розробник (іноді).
6. 🧩 Підсумок
Отже, що ми сьогодні розібрали?
- SPA — це ілюзія. Немає ніяких сторінок, є тільки зміна вмісту через JS.
- Проблема 404: Сервер за замовчуванням шукає фізичні файли, які відповідають URL.
- Рішення: Налаштувати сервер (Rewrite Rule), щоб на будь-який невідомий запит він віддавав
index.html. Це "чорний хід" для вашого додатку. - Ви тепер знаєте, як це налаштувати в Nginx та Express.
Ви більше не будете панікувати, коли побачите 404 на своєму продакшн-сервері. Ви просто посміхнетесь і додасте одну стрічку в конфіг.
🔜 У наступній серії: Ми навчилися віддавати контент. Але як переконатися, що його бачите тільки ви? Тема наступного уроку: JWT токени: Як носити паспорт у кишені браузера.
А поки що — це був CS50 (умовно). Гарного кодингу! 👋