Skip to main content

Різниця між AngularJS та Angular

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+)
Реліз20102016 (повне переписування)
МоваJavaScriptTypeScript
Зв'язування данихДвостороннє через $scope/digestОдностороннє @Input/@Output
АрхітектураMVC, контролери, директивиКомпоненти, модулі, сервіси
CLIВідсутній (bower/gulp вручну)Angular CLI (ng generate, ng build)
МобільніБазово (PhoneGap)Native-ready (Ionic, Cordova)
ПродуктивністьDigest-цикл гальмує на 1000+ watchersAOT + 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.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.

Дочитали статтю?