Модуль 25

Advanced Routing: Guards, Resolver, Lazy Loading

Чудовий вибір теми! Це саме той момент, коли ми переходимо від створення простих сторінок до архітектури серйозних, масштабованих веб-додатків.

Уявіть, що я стою на сцені 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).

Типові помилки:

  1. "Resolver для всього". Новачки часто пхають у резолвери важкі запити.
    • Чому це погано? Користувач клікає на посилання, і 5 секунд нічого не відбувається (бо роутер чекає). Користувач думає, що сайт зламався, і клікає ще 10 разів.
    • Як краще? Іноді краще пустити користувача на сторінку відразу і показати Skeleton Loader (сірі прямокутники), поки дані вантажаться. Resolver ідеальний для критичних даних, без яких сторінка не має сенсу.
  2. Безпека тільки на фронтенді.
    • Пам'ятайте: Ваш AuthGuard — це лише зручність для користувача. Будь-який хакер може вимкнути JavaScript або підробити запит. Справжня безпека завжди на сервері (Backend)!
  3. Циклічні перенаправлення.
    • Guard перекидає на Login -> Login бачить, що ви вже залогінені, і кидає назад -> Guard знову на Login. Браузер зависає. Завжди перевіряйте логіку!

Порада від Девіда:

"Завжди думайте про UX (досвід користувача). Чи приємно користувачу чекати? Чи зрозуміло йому, чому його не пускають? Технології — це інструмент, а не самоціль. Ваша мета — зробити так, щоб користувач відчував турботу, а не боротьбу з інтерфейсом."


6. 🧩 Підсумок

Отже, що ми сьогодні поклали до свого інструментарію?

  1. Ми навчилися оптимізувати завантаження додатку, розбиваючи його на шматки (Lazy Loading).
  2. Ми навчилися захищати маршрути та контролювати поведінку користувача (Guards).
  3. Ми навчилися готувати дані заздалегідь, щоб уникнути "стрибання" верстки (Resolvers).

Тепер ви не просто пишете код, який "якось працює". Ви керуєте потоками даних і користувачів як справжній архітектор.

👀 Тизер наступного уроку: Окей, ми навчилися завантажувати дані перед входом на сторінку. Але що робити, якщо ці дані потрібні в десяти різних компонентах одночасно? Невже передавати їх ланцюжком через всі рівні? О ні. Наступного разу ми поговоримо про State Management (Управління станом) — "глобальний мозок" вашого додатку.

А поки що — це був CS50... тобто, наш урок з Advanced Routing. Щасти вам з кодом!