Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Різниця між AngularJS та Angular». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**AngularJS (1.x)** - JavaScript MVC-фреймворк 2010 року з `$scope` і двостороннім зв'язуванням даних. **Angular (2+)** - TypeScript-фреймворк 2016 року, переписаний з нуля з AOT-компіляцією та Zone.js. ```js // AngularJS: $scope з'єднує контролер і шаблон $scope.message = 'Hi'; // стежить $digest() // Angular: властивість класу напряму в шаблоні message = 'Hi'; // Zone.js визначає зміни ``` **Ключове:** Angular - не оновлення AngularJS. Google переписав його з нуля у 2016 році. Спільна назва, різна кодова база.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**AngularJS (1.x)** та **Angular (2+)** - два різних фреймворки, які поділяють назву, але майже нічого більше. ## Теорія ### TL;DR - AngularJS (2010): JavaScript + контролери + `$scope`; Angular (2016): TypeScript + компоненти + декоратори - Головна різниця: digest-цикл через `$scope` проти Zone.js і дерева компонентів - Офіційна підтримка AngularJS завершилась у грудні 2021; Angular активно оновлюється, v18 вийшов у 2024 - Google переписав Angular з нуля у 2016 році, це не оновлення старого - AngularJS - тільки для підтримки legacy; Angular - для всього нового ### Швидкий приклад ```html <!-- AngularJS 1.x: двостороннє зв'язування через $scope --> <div ng-app="app" ng-controller="MainCtrl"> <input ng-model="name"> <p>Привіт, {{ name }}!</p> </div> ``` ```js angular.module('app', []).controller('MainCtrl', function($scope) { $scope.name = 'Світ'; // $digest() стежить за кожною зміною }); ``` ```typescript // Angular: клас компонента, $scope не потрібен @Component({ selector: 'app-hello', template: `<p>Привіт, {{ name }}!</p>` }) export class HelloComponent { name = 'Світ'; // властивість TypeScript-класу } ``` В AngularJS `$scope` - спільний об'єкт між контролером і шаблоном. Angular замінює це класом, властивості якого напряму відповідають даним шаблону. ### Ключова різниця AngularJS прив'язує глобальне дерево `$scope` до шаблонів і запускає цикли `$digest()`, перевіряючи кожен watcher на зміни. На маленьких застосунках це працює, але зі зростанням кількості watchers починає гальмувати. Angular компілює TypeScript у JavaScript ще до запуску (AOT), будує дерево компонентів і використовує Zone.js, щоб знати про завершення асинхронних операцій. Після цього перевіряє тільки зачеплену гілку. З `ChangeDetectionStrategy.OnPush` Angular взагалі пропускає гілки, де `@Input` не змінювався. Результат: бандли менші в 5-10 разів і помітно швидший рендеринг на великих застосунках. Я бачив команди, які підтримували AngularJS у 2023 році, бо міграція постійно відкладалась. Накладні витрати digest-циклу в тих застосунках були топ-1 скаргою з продуктивності, особливо на дашбордах із 500+ прив'язаними значеннями. ### Коли що використовувати - Старий внутрішній інструмент, бюджету на міграцію немає: AngularJS (не додавай нових фіч) - Новий SPA, корпоративний застосунок або мобільний проект: Angular - Команда з досвідом TypeScript: Angular (краща підтримка IDE і рефакторинг) - Вже на AngularJS і треба масштабуватись: `ngUpgrade` для поступової міграції ### Таблиця порівняння | Характеристика | AngularJS (1.x) | Angular (2+) | |---|---|---| | Реліз | 2010 | 2016 (повне переписування) | | Мова | JavaScript | TypeScript | | Зв'язування даних | Двостороннє через `$scope`/digest | Одностороннє `@Input`/`@Output` | | Архітектура | MVC, контролери, директиви | Компоненти, модулі, сервіси | | CLI | Відсутній (bower/gulp вручну) | Angular CLI (`ng generate`, `ng build`) | | Мобільні | Базово (PhoneGap) | Native-ready (Ionic, Cordova) | | Продуктивність | Digest-цикл гальмує на 1000+ watchers | AOT + lazy loading, OnPush detection | | Підтримка | Завершена у грудні 2021 | Активна, v18 у 2024 | | Коли використовувати | Тільки підтримка legacy | Нові проекти, enterprise | ### Як Angular визначає зміни AngularJS парсить HTML на `ng-*` директиви, будує дерево `$scope`, потім запускає `$digest()` до 10 разів поспіль, поки жоден watcher не спрацює. Кожен прохід - O(n) по всіх watchers. Angular іде інакшим шляхом: компілятор `ngc` будує шаблони компонентів у JavaScript на етапі збірки. Zone.js патчить браузерні API (`setTimeout`, `addEventListener`), щоб знати про завершення асинхронних операцій. Тільки після цього Angular перевіряє потрібне піддерево компонентів. ### Типові помилки **Мутація `$scope` всередині нативного таймера в AngularJS:** ```js // Погано: $digest не запускається, UI залишається застарілим setTimeout(function() { $scope.items.push('новий елемент'); }); // Правильно: обгортаємо в $apply $scope.$apply(function() { $scope.items.push('новий елемент'); }); ``` **Надмірне використання `[(ngModel)]` у формах Angular:** ```html <!-- Запускає повну перевірку змін при кожному натисканні клавіші --> <input [(ngModel)]="user.name"> ``` Для форм з багатьма полями краще використовувати реактивні форми з `FormControl` і `patchValue`. Менше перевірок, краща продуктивність. **Забутий імпорт модулів в Angular:** ```typescript @NgModule({ declarations: [MyComponent] // забули: imports: [CommonModule, FormsModule] }) ``` `*ngIf` і `*ngFor` живуть у `CommonModule`. Без нього шаблон видає помилку парсингу. Класична пастка для початківців. ### Де зустрічається в реальних проектах - Netflix перемістив компоненти дашборду з AngularJS на Angular для зменшення розміру бандлів - Google Workspace використовує Angular-модулі для ізоляції фіч у великих командах - Delta Airlines побудував SPA для бронювання на Angular CLI - IBM замінив promise-ланцюжки AngularJS на Angular + RxJS для реактивних форм ### Питання на співбесіді **Q:** Чому Google переписав Angular замість оновлення AngularJS? **A:** Архітектура `$scope`/digest не підтримувала AOT-компіляцію та серверний рендеринг без повного редизайну. Написати з нуля виявилось простіше. **Q:** Що таке `$digest`-цикл і чому він гальмує великі застосунки? **A:** Він перебирає всі зареєстровані watchers, порівнюючи поточні й попередні значення. Якщо щось змінилось, починає заново, до 10 разів. На сотнях watchers це дорого коштує при кожній взаємодії. **Q:** Як Zone.js замінює `$scope.$apply`? **A:** Zone.js патчить `setTimeout`, `Promise` і DOM-події. Angular використовує ці патчі, щоб знати про завершення асинхронних операцій, і запускає перевірку змін автоматично. В AngularJS доводилось викликати `$apply` вручну для коду поза фреймворком. **Q:** Як мігрувати великий AngularJS-застосунок на Angular? **A:** Через `ngUpgrade`: запустити обидва фреймворки разом, потім мігрувати компоненти на Angular по одному. AngularJS і Angular-компоненти комунікують під час переходу через спільні сервіси. **Q:** Що таке сигнали (signals) в Angular і навіщо вони? **A:** Signals (стабільні в Angular 18) дають точну реактивність без Zone.js. Сигнал відстежує, які саме компоненти від нього залежать, і при зміні значення перерендерює тільки їх, а не все дерево. ## Приклади ### Hello World в обох фреймворках ```html <!-- AngularJS 1.x --> <div ng-app="app" ng-controller="MainCtrl"> <p>{{ message }}</p> </div> <script> angular.module('app', []).controller('MainCtrl', function($scope) { $scope.message = 'Привіт від AngularJS'; // контролер прив'язується через $scope }); </script> ``` ```typescript // Angular: властивість класу замінює $scope @Component({ selector: 'app-root', template: `<p>{{ message }}</p>` }) export class AppComponent { message = 'Привіт від Angular'; } ``` Два синтаксиси, дві ментальні моделі. AngularJS з'єднує контролер і шаблон через `$scope`. Angular використовує TypeScript-клас, де властивості стають даними шаблону напряму, без спільного мутабельного об'єкта між ними. ### Список задач: `ng-repeat` проти `*ngFor` ```html <!-- AngularJS: ng-repeat під наглядом $digest --> <ul> <li ng-repeat="todo in todos">{{ todo.text }}</li> </ul> ``` ```js $scope.todos = [{text: 'Купити молоко'}]; $scope.todos.push({text: 'Вигуляти собаку'}); // $digest бачить зміну масиву, ре-рендер ``` ```typescript // Angular: trackBy запобігає зайвому відтворенню DOM @Component({ template: ` <ul> <li *ngFor="let todo of todos; trackBy: trackById">{{ todo.text }}</li> </ul> <input [(ngModel)]="newTodo" (keyup.enter)="add()"> ` }) export class TodosComponent { todos = [{id: 1, text: 'Купити молоко'}]; newTodo = ''; trackById(index: number, todo: {id: number}) { return todo.id; } add() { this.todos.push({id: Date.now(), text: this.newTodo}); this.newTodo = ''; } } ``` `trackBy` підказує Angular, які `<li>` можна перевикористати при оновленні списку. Без неї Angular знищує і відтворює кожен елемент заново. В AngularJS будь-яка мутація масиву запускає повний ре-рендер списку через `$digest`.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.