Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Що таке Angular?». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Angular** - це TypeScript-фреймворк від Google для створення динамічних веб-додатків. ```typescript @Component({ selector: 'app-root', template: `<h1>{{ title }}</h1>` }) export class AppComponent { title = 'Привіт Angular'; } ``` **Ключове:** Angular включає маршрутизацію, форми, HTTP-клієнт і систему впровадження залежностей (DI) з коробки. React потребує окремих пакетів для всього цього. Angular обирають, коли команда або кодова база виростає за межі кількох компонентів.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Angular** - це платформа на базі TypeScript від Google для створення динамічних клієнтських веб-додатків. Використовує компонентну архітектуру, вбудоване впровадження залежностей (dependency injection) і реактивне зв'язування даних. ## Теорія ### TL;DR - Angular - як набір міського планувальника: готові блоки (компоненти) з чіткими правилами з'єднання (dependency injection), щоб додаток не розвалювався при масштабуванні - Головна різниця від React: Angular - повний фреймворк із маршрутизацією, формами та HTTP-клієнтом з коробки; React - UI-бібліотека, до якої треба докупляти пакети - Команда більше 5 осіб або великий проект: Angular. Прототип або малий стартап: React чи Vue - TypeScript за замовчуванням - помилки ловиш на етапі компіляції, не в браузері о другій ночі - Zone.js і Ivy-рендерер контролюють change detection самостійно: змінюєш дані, DOM оновлюється ### Швидкий приклад ```typescript // Базовий Angular-компонент - app.component.ts import { Component } from '@angular/core'; @Component({ selector: 'app-root', // рендериться в HTML як <app-root> template: ` <h1>{{ title }}</h1> <button (click)="update()">Натисни</button> ` }) export class AppComponent { title = 'Привіт Angular'; // прив'язане до шаблону update() { this.title = 'Оновлено!'; // автоматично оновлює DOM } } // Жодного document.querySelector. // Angular стежить за title і перерендерює h1 при кожній зміні. ``` Декоратор `@Component` говорить Angular, що це компонент, який HTML-тег він займає і що рендерити. Змінюєш `title` у класі - браузер оновлюється миттєво. Жодних DOM-маніпуляцій вручну. ### Angular проти бібліотеки React дає тобі функцію рендерингу і каже: решта на твій розсуд. Angular поставляється з маршрутизацією (`@angular/router`), HTTP-клієнтом (`HttpClient`), формами (`ReactiveFormsModule`), анімацією та CLI, який розгортає повний проект однією командою. Це і є ключовий компроміс: більше рішень прийнято заздалегідь, менше хаосу у великій команді. На практиці це відчутно. Команда з 10 розробників на React витратить тижні, домовляючись яку бібліотеку форм взяти, який стейт-менеджер і як організувати папки. В Angular команда відкриває ті самі файли в тих самих папках з першого дня. За моїм спостереженням, саме ця узгодженість скорочує онбординг на великих проектах більше, ніж будь-яка технічна фіча. ### Коли використовувати - Enterprise-дашборд із auth guards і lazy-loaded модулями: Angular - Команда більше 10 розробників: Angular (TypeScript рятує при роботі з великою кодовою базою) - Мобільний додаток із нативними компонентами: Angular + Ionic - Малий прототип або MVP: React чи Vue запустяться швидше - Рефакторинг старого jQuery-коду: Angular-директиви замінять більшість ручних DOM-маніпуляцій ### Порівняння | Характеристика | Angular | React | Vue | |----------------|---------|-------|-----| | Тип | Повний фреймворк | UI-бібліотека | Фреймворк | | Мова | TypeScript | JS/TS | JS/TS | | Розмір Hello World | ~65KB gzip | ~35KB | ~20KB | | Крива навчання | 1-2 тижні | ~3 дні | ~1 день | | Вбудований роутер | Так | Ні (React Router) | Так | | Вбудовані форми | Reactive + template | Ні (Formik, RHF) | Ні | | Коли обирати | 50+ компонентів, enterprise | Гнучкий стек, кастомний UI | Швидкий старт | ### Як працює Ivy і Zone.js Браузер завантажує `index.html` із тегом `<app-root>`. Angular CLI компілює TypeScript у JS-бандли через webpack, Ivy-рендерер (за замовчуванням з Angular 9) розбирає декоратори `@Component` і будує дерево компонентів у пам'яті. Zone.js патчить браузерні асинхронні API - `setTimeout`, Promise, обробники подій - щоб Angular знав, коли запускати change detection. Оновлення DOM відбуваються в мікрозадачах, без перезавантаження сторінки. Ivy зменшив бандли приблизно на 40% порівняно зі старим View Engine: він компілює компоненти незалежно, а не за один глобальний прохід для всього застосунку. Тому й перезбірки в режимі розробки стали помітно швидшими. ### Типові помилки **1. `*ngFor` без `trackBy`** ```html <!-- ПОГАНО: Angular знищує і відтворює всі елементи при кожній зміні масиву --> <li *ngFor="let user of users">{{ user.name }}</li> <!-- ДОБРЕ: Angular перевикористовує DOM-вузли за ідентифікатором --> <li *ngFor="let user of users; trackBy: trackById">{{ user.name }}</li> ``` На списках із 500+ елементів різниця у швидкості рендерингу помітна без профайлера. **2. Сервіс у `providers` компонента замість root** ```typescript // ПОГАНО: новий екземпляр UserService на кожен компонент - ламає спільний стан @Component({ providers: [UserService] }) // ДОБРЕ: один синглтон на весь застосунок @Injectable({ providedIn: 'root' }) export class UserService {} ``` **3. Відсутній `async` pipe для Observables** ```html <!-- ПОГАНО: падає, коли Observable ще не емітував значення --> <div>{{ (user$ | async).name }}</div> <!-- ДОБРЕ: безпечна навігація обробляє undefined --> <div *ngIf="user$ | async as user">{{ user.name }}</div> ``` **4. OnPush change detection із мутацією об'єкта** ```typescript // ПОГАНО: той самий об'єктний референс - OnPush пропускає перевірку, UI застаріває this.data.value = 'new'; // UI не оновиться! // ДОБРЕ: новий референс запускає перевірку this.data = { ...this.data, value: 'new' }; ``` Це ловить навіть досвідчених розробників, які звикли до мутабельного стану. ### Де використовується - Google Workspace: 100+ Angular-компонентів із lazy-loaded модулями - Microsoft Office Online: Angular 16+ для спільної роботи в реальному часі (Nx monorepo) - Forbes.com: Angular Universal (SSR) для серверного рендерингу та SEO - NBA.com: RxJS-стріми для live-оновлення рахунків - PayPal checkout: кастомні Angular-директиви для обробки платіжних форм ### Питання для співбесіди **Q:** Яка різниця між template-driven і reactive forms? **A:** Template-driven форми використовують `ngModel` для двостороннього зв'язування і підходять для простих випадків. Reactive форми будуються через `FormBuilder` у TypeScript: вони незмінні, легко тестуються і добре масштабуються на складну валідацію. **Q:** Поясни ієрархію dependency injection в Angular. **A:** `providedIn: 'root'` створює один синглтон для всього застосунку. `providers: []` у компоненті створює новий екземпляр, видимий лише для цього компонента і його дочірніх елементів. Lazy-loaded модулі отримують власний інжектор. Розуміння цієї ієрархії пояснює більшість помилок типу "чому дані в сервісі застаріли". **Q:** Що покращив Ivy-рендерер порівняно зі старим View Engine? **A:** Ivy компілює кожен компонент незалежно замість одного глобального проходу для всього застосунку. Це зменшує бандли приблизно на 40%, покращує tree-shaking і прискорює інкрементні перезбірки в розробці. **Q:** Навіщо Angular потрібен Zone.js? **A:** Zone.js патчить браузерні асинхронні API, щоб Angular автоматично запускав change detection після завершення асинхронної роботи. Без нього треба було б вручну викликати `ChangeDetectorRef.detectChanges()` після кожного HTTP-запиту, таймера або події. **Q:** Senior-питання: з `ChangeDetectionStrategy.OnPush` і `async` pipe - чи оновиться компонент при новому значенні з NgRx-селектора? Чому? **A:** Так. `async` pipe всередині викликає `markForCheck()` при кожному новому значенні Observable. NgRx `select` повертає новий Observable-референс при зміні стану. Тому навіть з `OnPush` компонент позначається як такий, що потребує перевірки, і Angular включає його в наступний цикл change detection. ## Приклади ### Базовий: лічильник кліків ```typescript // counter.component.ts import { Component } from '@angular/core'; @Component({ selector: 'app-counter', template: ` <p>Лічильник: {{ count }}</p> <button (click)="increment()">+1</button> ` }) export class CounterComponent { count = 0; increment() { this.count++; // Angular оновлює <p> автоматично } } ``` Основний цикл: дія користувача викликає метод, метод змінює дані, Angular перерендерює шаблон. Жодних DOM-запитів. ### Середній: список користувачів із API ```typescript // user.service.ts import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class UserService { constructor(private http: HttpClient) {} getUsers(): Observable<any[]> { return this.http.get<any[]>('https://jsonplaceholder.typicode.com/users'); } } // users.component.ts @Component({ template: ` <ul> <li *ngFor="let user of users; trackBy: trackById"> {{ user.name }} </li> </ul> ` }) export class UsersComponent { users: any[] = []; constructor(private userService: UserService) { this.userService.getUsers().subscribe(data => this.users = data); } trackById(index: number, user: any) { return user.id; // запобігає повному перерендерингу списку при зміні одного елемента } } // Виводить 10 імен із API. UserService вводиться автоматично через DI. ``` `UserService` оголошено один раз із `providedIn: 'root'` - кожен компонент, якому він потрібен, отримує той самий екземпляр без додаткових налаштувань. ### Просунутий: OnPush з незмінними даними ```typescript // Цей патерн ловить більшість middle-розробників хоча б раз. @Component({ changeDetection: ChangeDetectionStrategy.OnPush // перевіряє тільки при зміні референсу @Input }) export class UserCardComponent { @Input() user: { name: string }; } // Батьківський компонент - НЕПРАВИЛЬНО: this.user.name = 'Яна'; // той самий референс - OnPush пропускає, UI застаріє! // ПРАВИЛЬНО: новий об'єктний референс this.user = { ...this.user, name: 'Яна' }; // OnPush виявляє зміну, оновлює UI // Те саме для масивів: // НЕПРАВИЛЬНО: this.items.push(newItem) // ПРАВИЛЬНО: this.items = [...this.items, newItem] ``` `OnPush` - це оптимізація: Angular пропускає компонент під час change detection, якщо жоден із його `@Input` не змінив референс. У великих деревах компонентів це вдвічі скорочує зайвий рендеринг, але вимагає незмінного (immutable) підходу до даних.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.