# Angular 19 Zoneless: Hiệu năng và Change Detection không cần Zone.js > Angular zoneless change detection loại bỏ Zone.js để mang lại bundle nhỏ hơn, rendering nhanh hơn và mô hình reactivity tường minh thông qua signals. Hướng dẫn chuyên sâu về quá trình chuyển đổi từ Zone.js sang zoneless Angular, từ API experimental trong Angular 19 đến API stable trong Angular 20+. - Published: 2026-04-27 - Updated: 2026-04-27 - Author: SharpSkill - Tags: angular, zoneless, change-detection, performance, signals, zone-js - Reading time: 10 min --- Angular 19 đánh dấu một bước ngoặt quan trọng trong lịch sử phát triển của framework khi giới thiệu chế độ Zoneless — cho phép ứng dụng hoạt động hoàn toàn không cần Zone.js. Kể từ khi ra đời, Zone.js luôn đóng vai trò trung tâm trong cơ chế Change Detection của Angular, tuy nhiên nó cũng mang theo những hạn chế đáng kể về hiệu năng và kích thước bundle. Với Angular 19, đội ngũ phát triển đã cung cấp một giải pháp thay thế dựa trên Signals, mở ra kỷ nguyên mới cho việc xây dựng ứng dụng Angular nhanh hơn, nhẹ hơn và dễ debug hơn. > **Zoneless đã ổn định từ Angular 20** > > Chế độ Zoneless được giới thiệu ở trạng thái experimental trong Angular 19 với API `provideExperimentalZonelessChangeDetection()`. Từ Angular 20 trở đi, API này đã chính thức ổn định với tên gọi `provideZonelessChangeDetection()`. Các dự án mới hoàn toàn có thể áp dụng ngay từ đầu, trong khi các dự án hiện tại nên lên kế hoạch chuyển đổi dần dần. ## Zone.js là gì và tại sao cần loại bỏ nó? Zone.js là một thư viện đã gắn liền với Angular từ phiên bản 2. Nó hoạt động bằng cách monkey-patch hơn 130 API trình duyệt — bao gồm `setTimeout`, `Promise`, `addEventListener`, `XMLHttpRequest` và nhiều API khác — để tạo ra một "execution context" cho phép Angular biết khi nào cần chạy Change Detection. Mỗi khi một callback bất đồng bộ được thực thi trong zone của Angular, framework sẽ tự động kích hoạt một chu kỳ Change Detection trên toàn bộ cây component. Cơ chế này tuy tiện lợi nhưng gây ra nhiều vấn đề nghiêm trọng: - **Kích thước bundle tăng thêm ~33KB** (raw) hoặc ~10KB (gzip) chỉ riêng cho Zone.js - **Change Detection chạy quá nhiều lần không cần thiết**, đặc biệt trong các ứng dụng phức tạp với nhiều thao tác bất đồng bộ - **Stack trace bị "ô nhiễm"** bởi 8-12 frame thừa từ Zone.js, khiến việc debug trở nên khó khăn - **Xung đột với các thư viện bên thứ ba** không tương thích với cơ chế patching của Zone.js Dưới đây là cấu hình truyền thống sử dụng Zone.js: ```typescript // app.config.ts - Traditional Zone.js setup (Angular 18-19) import { ApplicationConfig } from '@angular/core'; import { provideZoneChangeDetection } from '@angular/core'; export const appConfig: ApplicationConfig = { providers: [ provideZoneChangeDetection({ eventCoalescing: true }), // Zone.js patches ~130+ browser APIs // Every async callback triggers change detection ] }; ``` Với mỗi sự kiện click, mỗi HTTP request hoàn thành, mỗi `setTimeout` kết thúc — Zone.js đều thông báo cho Angular và kích hoạt kiểm tra toàn bộ cây component. Trong một ứng dụng enterprise với hàng trăm component, điều này dẫn đến hàng trăm chu kỳ Change Detection không cần thiết cho mỗi tương tác của người dùng. ## Kích hoạt chế độ Zoneless trong Angular 19 Việc chuyển sang chế độ Zoneless khá đơn giản. Trong Angular 19, API được đánh dấu là experimental: ```typescript // app.config.ts - Angular 19 (experimental) import { ApplicationConfig } from '@angular/core'; import { provideExperimentalZonelessChangeDetection } from '@angular/core'; export const appConfig: ApplicationConfig = { providers: [ provideExperimentalZonelessChangeDetection(), // No more Zone.js patching ] }; ``` Từ Angular 20 trở đi, API đã được ổn định hóa: ```typescript // app.config.ts - Angular 20+ (stable) import { ApplicationConfig } from '@angular/core'; import { provideZonelessChangeDetection } from '@angular/core'; export const appConfig: ApplicationConfig = { providers: [ provideZonelessChangeDetection(), ] }; ``` Sau khi cập nhật cấu hình, cần loại bỏ Zone.js khỏi dự án: ```bash # Remove zone.js polyfill from angular.json build and test targets # Then uninstall the package npm uninstall zone.js ``` Cần lưu ý rằng việc loại bỏ Zone.js cũng đồng nghĩa với việc phải xóa các tham chiếu đến `zone.js/testing` trong file cấu hình test, cũng như loại bỏ entry `zone.js` trong mảng `polyfills` của `angular.json`. ## So sánh hiệu năng: Zone.js và Zoneless Sự khác biệt về hiệu năng giữa hai chế độ là rất đáng kể, đặc biệt trong các ứng dụng quy mô lớn: | Metric | Zone.js | Zoneless | Improvement | |--------|---------|----------|-------------| | Initial bundle size | +33KB raw / +10KB gzip | 0KB overhead | 100% reduction | | Change detection cycles (typical app) | 150-300 per interaction | 5-15 per interaction | 80-95% fewer | | Stack trace depth | 8-12 extra Zone frames | Clean native traces | Immediate clarity | | Time to Interactive (TTI) | Baseline | 15-25% faster | Zone bootstrap eliminated | Những con số trên cho thấy chế độ Zoneless không chỉ giảm kích thước bundle mà còn cải thiện đáng kể trải nghiệm runtime. Việc giảm 80-95% số chu kỳ Change Detection đồng nghĩa với việc CPU của người dùng được giải phóng đáng kể, đặc biệt quan trọng trên các thiết bị di động có cấu hình thấp. ## Signals — nền tảng của Zoneless Change Detection Signals là cơ chế reactivity được Angular giới thiệu từ phiên bản 16 và trở thành trụ cột chính của chế độ Zoneless. Thay vì dựa vào Zone.js để "đoán" khi nào dữ liệu thay đổi, Signals cho phép Angular biết **chính xác** giá trị nào đã thay đổi và component nào cần cập nhật. ```typescript // counter.component.ts - Signal-driven change detection import { Component, signal, computed } from '@angular/core'; @Component({ selector: 'app-counter', template: `
Double: {{ doubled() }}
Results for: {{ searchTerm() }}
` }) export class SearchComponent { searchControl = new FormControl(''); // Convert observable to signal for automatic template updates searchTerm = toSignal( this.searchControl.valueChanges.pipe( debounceTime(300), distinctUntilChanged() ), { initialValue: '' } ); } ``` Hàm `toSignal()` từ `@angular/core/rxjs-interop` đóng vai trò cầu nối giữa thế giới RxJS và Signals. Nó tự động subscribe Observable và cập nhật signal mỗi khi có giá trị mới, đồng thời tự động unsubscribe khi component bị destroy. Đây là pattern quan trọng nhất khi làm việc với Reactive Forms trong chế độ Zoneless. ## Xử lý SSR và tác vụ bất đồng bộ với PendingTasks Trong chế độ Zoneless, Angular không còn tự động theo dõi các tác vụ bất đồng bộ thông qua Zone.js. Điều này đặc biệt quan trọng với Server-Side Rendering (SSR), nơi Angular cần biết khi nào tất cả dữ liệu đã sẵn sàng trước khi serialize HTML. Angular cung cấp service `PendingTasks` để giải quyết vấn đề này: ```typescript // data-loader.component.ts - SSR-compatible async loading import { Component, inject, signal } from '@angular/core'; import { PendingTasks } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { firstValueFrom } from 'rxjs'; @Component({ selector: 'app-data-loader', template: ` @if (data()) {