Чудовий вибір теми! Це саме той момент, коли ми переходимо від створення простих сторінок до архітектури серйозних, масштабованих веб-додатків.
Уявіть, що я стою на сцені Sanders Theatre, закочую рукави, і ми починаємо!
🎓 Урок: Advanced Routing — Охоронці, Помічники та Стратеги
(Guards, Resolvers, Lazy Loading)
1. 🔥 Вступ: Чому ваш додаток гальмує і куди дивляться охоронці?
Давайте почнемо з простого запитання: Ви коли-небудь заходили в гігантський торговий центр, щоб купити лише одну пару шкарпеток?
Уявіть, що на вході в ТРЦ вас змушують: 1. Одягнути на себе весь асортимент одягу всіх магазинів (на всяк випадок, раптом знадобиться). 2. Показати паспорт, довідку про доходи та членський квиток, навіть якщо ви просто йдете в туалет. 3. Почекати в коридорі, поки продавці розкладуть товар на полицях, перш ніж вас пустять у відділ.
Звучить абсурдно, правда? Але саме так працюють погано спроєктовані веб-додатки! * Вони завантажують весь код одразу (немає Lazy Loading). * Вони пускають користувача на сторінку, а потім викидають, бо він не залогінений (немає Guards). * Вони показують напівпорожню сторінку, де все смикається, поки долітають дані (немає Resolvers).
Сьогодні ми це виправимо. Ми навчимо наш роутер (маршрутизатор) бути розумним менеджером, а не просто вахтером.
2. 🧠 Теоретична база: Що під капотом?
Давайте розберемо трьох китів просунутого роутингу. Я буду використовувати термінологію Angular (бо там це канонічно), але логіка ідентична для React, Vue та інших.
1. Lazy Loading (Ліниве завантаження) 🦥
- Як було: Весь ваш JavaScript (адмінка, налаштування, головна) збивається в один величезний файл (
main.bundle.js). Браузер качає 5 МБ коду, щоб показати "Привіт, світ". - Як треба: Ми розрізаємо додаток на шматки (chunks). Код "Адмінки" завантажується тільки тоді, коли користувач клікає на посилання
/admin. - Аналогія: Ви не купуєте меблі для дитячої кімнати, поки у вас не з'явилися діти. Ви купуєте їх на вимогу.
2. Guards (Охоронці / Ґарди) 🛡️
Це функції, які запускаються перед тим, як роутер дозволить перехід. * CanActivate: "Стій! Ти хто? У тебе є токен доступу?" (Захист маршруту). * CanDeactivate: "Ей, ти не зберіг форму! Ти точно хочеш піти і втратити дані?" (Запобігання випадковому виходу). * Аналогія: Фейс-контроль у клубі. Якщо ти в кросівках (немає прав адміна) — тебе не пустять у VIP-зону.
3. Resolvers (Резолвери) 📦
Це "попереднє завантаження даних". Роутер каже: "Я не покажу тобі компонент Профілю, поки не отримаю дані про користувача з сервера". * Як працює: Ви клікаєте посилання -> Роутер робить запит на API -> Чекає відповідь -> І тільки тоді малює сторінку вже з даними. * Аналогія: Особистий асистент, який заходить у готельний номер перед вами, вмикає світло, наповнює міні-бар, і тільки тоді запрошує вас увійти.
3. 🧪 Приклади: Від коду до магії
Давайте подивимось на це в контексті коду (на прикладі Angular/TypeScript, оскільки тут ці патерни найяскравіші).
Сценарій 1: Lazy Loading
Що ви очікуєте побачити? Ми хочемо, щоб модуль налаштувань не вантажився відразу.
// app-routing.module.ts
const routes: Routes = [
{
path: 'home',
component: HomeComponent // Завантажується одразу (Eager)
},
{
path: 'settings',
// 🔥 МАГІЯ ТУТ: import() розбиває код на окремий чанк
loadChildren: () => import('./settings/settings.module').then(m => m.SettingsModule)
}
];
Результат: Коли ви відкриваєте Network у браузері, ви побачите, що файл settings-module.js завантажиться тільки після кліку на "Settings". Ми зекономили трафік!
Сценарій 2: Guard (Охорона)
Задача: Не пускати в /admin тих, хто не авторизований.
// auth.guard.ts
@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router) {}
canActivate(): boolean {
if (this.authService.isLoggedIn()) {
return true; // ✅ Проходь, друже!
} else {
this.router.navigate(['/login']); // 🛑 Стоп! Йди логінитись.
return false;
}
}
}
// У роутері:
{ path: 'admin', component: AdminComponent, canActivate: [AuthGuard] }
Сценарій 3: Resolver (Комфорт)
Задача: Ми відкриваємо сторінку товару /product/42. Ми не хочемо бачити порожні поля, поки вантажиться назва і ціна.
// product.resolver.ts
@Injectable({ providedIn: 'root' })
export class ProductResolver implements Resolve<Product> {
constructor(private productService: ProductService) {}
resolve(route: ActivatedRouteSnapshot): Observable<Product> {
// Ми повертаємо потік даних. Роутер чекатиме, поки він завершиться!
return this.productService.getProductById(route.params['id']);
}
}
// У компоненті товару нам вже не треба робити запит, дані вже тут:
ngOnInit() {
this.product = this.route.snapshot.data['product']; // Дані готові!
}
4. 🛠 Практична частина
Час "забруднити руки". Ось ваші завдання. Не поспішайте, думайте логічно.
Завдання 1: "VIP кімната" (Guards)
Створіть маршрут /dashboard. Напишіть Guard, який перевіряє роль користувача.
* Якщо роль admin — пускає.
* Якщо роль user — перенаправляє на /home і виводить alert("Доступ заборонено").
Завдання 2: "Не йди від мене" (CanDeactivate)
Створіть сторінку з текстовим полем (input).
* Якщо користувач щось ввів, але не натиснув "Зберегти" і намагається піти на іншу сторінку — покажіть стандартне браузерне вікно підтвердження: "Ви маєте незбережені зміни. Піти?".
Завдання 3: "Затримка" (Resolver)
Створіть Resolver, який емулює затримку сервера (використайте delay(3000)).
* Прив'яжіть його до маршруту.
* Подивіться, що відбувається з інтерфейсом протягом цих 3 секунд.
* Питання на подумати: Чи добре це для користувача — дивитися на "завислий" екран 3 секунди без індикатора завантаження?
Завдання 4: Міні-кейс "Інтернет-магазин" У вас є структура: Головна, Каталог, Кошик, Оплата. * Які з цих сторінок варто завантажувати "ліниво" (Lazy)? * Де потрібен Guard? * Де згодиться Resolver? (Напишіть короткий план архітектури).
5. 💡 Мислення як у розробника
Ось де ми відокремлюємо новачків (juniors) від профі (seniors).
Типові помилки:
- "Resolver для всього". Новачки часто пхають у резолвери важкі запити.
- Чому це погано? Користувач клікає на посилання, і 5 секунд нічого не відбувається (бо роутер чекає). Користувач думає, що сайт зламався, і клікає ще 10 разів.
- Як краще? Іноді краще пустити користувача на сторінку відразу і показати Skeleton Loader (сірі прямокутники), поки дані вантажаться. Resolver ідеальний для критичних даних, без яких сторінка не має сенсу.
- Безпека тільки на фронтенді.
- Пам'ятайте: Ваш
AuthGuard— це лише зручність для користувача. Будь-який хакер може вимкнути JavaScript або підробити запит. Справжня безпека завжди на сервері (Backend)!
- Пам'ятайте: Ваш
- Циклічні перенаправлення.
- Guard перекидає на Login -> Login бачить, що ви вже залогінені, і кидає назад -> Guard знову на Login. Браузер зависає. Завжди перевіряйте логіку!
Порада від Девіда:
"Завжди думайте про UX (досвід користувача). Чи приємно користувачу чекати? Чи зрозуміло йому, чому його не пускають? Технології — це інструмент, а не самоціль. Ваша мета — зробити так, щоб користувач відчував турботу, а не боротьбу з інтерфейсом."
6. 🧩 Підсумок
Отже, що ми сьогодні поклали до свого інструментарію?
- Ми навчилися оптимізувати завантаження додатку, розбиваючи його на шматки (Lazy Loading).
- Ми навчилися захищати маршрути та контролювати поведінку користувача (Guards).
- Ми навчилися готувати дані заздалегідь, щоб уникнути "стрибання" верстки (Resolvers).
Тепер ви не просто пишете код, який "якось працює". Ви керуєте потоками даних і користувачів як справжній архітектор.
👀 Тизер наступного уроку: Окей, ми навчилися завантажувати дані перед входом на сторінку. Але що робити, якщо ці дані потрібні в десяти різних компонентах одночасно? Невже передавати їх ланцюжком через всі рівні? О ні. Наступного разу ми поговоримо про State Management (Управління станом) — "глобальний мозок" вашого додатку.
А поки що — це був CS50... тобто, наш урок з Advanced Routing. Щасти вам з кодом!