Tại sao Angular 18 lại là cuộc cách mạng về hiệu suất?
Nếu từng “vật lộn” với Angular đời cũ, chắc hẳn file app.module.ts là nỗi ám ảnh của bạn. Đó là nơi tập trung mọi thứ từ component đến service, khiến dự án càng lớn thì file này càng phình to và khó bảo trì. Chưa kể, cơ chế Change Detection dựa trên Zone.js thường gây lãng phí tài nguyên. Nó buộc framework phải quét lại toàn bộ cây component dù chỉ có một sự kiện nhỏ xảy ra.
Angular 18 ra đời để chấm dứt những phiền toái đó. Với Standalone Components, chúng ta chính thức khai tử NgModule rườm rà. Trong khi đó, Signals trở thành “trợ lý” đắc lực giúp quản lý state minh bạch hơn. Trong dự án thực tế với 5 developer vừa qua, mình áp dụng bộ đôi này và giúp productivity tăng khoảng 30%. Đặc biệt, lỗi kinh điển ExpressionChangedAfterItHasBeenCheckedError gần như biến mất hoàn toàn.
Khái niệm cốt lõi: Standalone và Signals
1. Standalone Components: Cấu trúc gọn nhẹ
Trước đây, một component muốn chạy phải được “đăng ký hộ khẩu” trong một module. Giờ đây, mỗi component là một thực thể độc lập. Nó tự quản lý các dependency (imports) cần thiết. Cách tiếp cận này giúp việc chia sẻ component hoặc triển khai Lazy Loading trở nên cực kỳ đơn giản. Code của bạn sẽ sạch hơn vì không còn những file module trung gian dư thừa.
2. Signals: Phản ứng nhanh như chớp
Hãy tưởng tượng Signal giống như một ô tính trong Excel. Khi giá trị ô A thay đổi, ô B tự động cập nhật mà không cần bạn can thiệp thủ công. Signals giúp Angular biết chính xác component nào cần render lại. Framework không còn phải đoán mò hay quét toàn bộ ứng dụng như trước. Đây chính là chìa khóa để ứng dụng đạt tốc độ phản hồi tức thì.
Thực hành: Xây dựng Todo List chuẩn Angular 18
Lý thuyết thôi là chưa đủ. Hãy cùng bắt tay xây dựng một ứng dụng quản lý công việc để thấy rõ sự khác biệt của kiến trúc mới này.
Bước 1: Khởi tạo dự án
Đầu tiên, hãy nâng cấp Angular CLI lên bản mới nhất. Mở terminal và thực thi lệnh:
npm install -g @angular/cli@latest
ng new angular-18-signals --standalone --routing --style=scss
cd angular-18-signals
Từ phiên bản 17, flag --standalone đã là mặc định. Tuy nhiên, mình vẫn ghi rõ để nhấn mạnh rằng NgModule đã lùi vào dĩ vãng.
Bước 2: Triển khai Logic với Signals
Chúng ta sẽ tạo todo-list.component.ts. Đây là nơi sức mạnh của Signal được thể hiện rõ nhất qua cách quản lý danh sách công việc:
import { Component, signal, computed, effect } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
@Component({
selector: 'app-todo-list',
standalone: true,
imports: [CommonModule, FormsModule],
templateUrl: './todo-list.component.html',
styleUrl: './todo-list.component.scss'
})
export class TodoListComponent {
// Khởi tạo Signal với dữ liệu mẫu
todoList = signal<{ task: string, completed: boolean }[]>([
{ task: 'Học Angular 18', completed: false },
{ task: 'Tối ưu hiệu suất app', completed: true }
]);
newTodo = signal('');
// Computed Signal: Tự động tính toán khi todoList thay đổi
completedCount = computed(() =>
this.todoList().filter(t => t.completed).length
);
constructor() {
// Effect: Tự động chạy khi có bất kỳ Signal nào bên trong thay đổi
effect(() => {
console.log(`Số lượng task hiện tại: ${this.todoList().length}`);
});
}
addTodo() {
if (this.newTodo().trim()) {
this.todoList.update(todos => [
...todos,
{ task: this.newTodo(), completed: false }
]);
this.newTodo.set('');
}
}
toggleTodo(index: number) {
this.todoList.update(todos =>
todos.map((todo, i) =>
i === index ? { ...todo, completed: !todo.completed } : todo
)
);
}
}
Bước 3: Tối ưu Template với Control Flow mới
Trong file HTML, hãy chú ý cách gọi Signal bằng cặp ngoặc (). Ngoài ra, cú pháp @for mới sẽ thay thế cho *ngFor cồng kềnh:
<div class="container">
<h2>My Tasks</h2>
<div class="input-group">
<input [ngModel]="newTodo()" (ngModelChange)="newTodo.set($event)" placeholder="Việc cần làm...">
<button (click)="addTodo()">Thêm</button>
</div>
<p>Hoàn thành: {{ completedCount() }} / {{ todoList().length }}</p>
<ul>
@for (item of todoList(); track $index) {
<li (click)="toggleTodo($index)" [class.done]="item.completed">
{{ item.task }}
</li>
}
</ul>
</div>
Cú pháp @for không chỉ dễ đọc hơn mà còn tối ưu thuật toán diffing, giúp render danh sách lớn nhanh hơn đáng kể.
Đỉnh cao hiệu suất: Zoneless Angular
Tính năng “đắt giá” nhất của Angular 18 chính là khả năng chạy không cần Zone.js. Khi loại bỏ thư viện này, ứng dụng của bạn sẽ giảm được khoảng 13KB (gzipped) dung lượng bundle. Quan trọng hơn, trình duyệt không còn bị chặn bởi các tác vụ kiểm tra ngầm liên tục.
Để kích hoạt, hãy cấu hình trong app.config.ts:
import { ApplicationConfig, provideExperimentalZonelessChangeDetection } from '@angular/core';
export const appConfig: ApplicationConfig = {
providers: [
provideExperimentalZonelessChangeDetection()
]
};
Sau bước này, bạn có thể tự tin gỡ bỏ zone.js khỏi file polyfills. Ứng dụng giờ đây sẽ hoạt động cực kỳ mượt mà nhờ cơ chế thông báo thay đổi trực tiếp từ Signals.
Lời khuyên từ thực tế
Nhiều bạn thắc mắc: “Liệu Signals có thay thế RxJS?”. Câu trả lời là không. Signals cực kỳ giỏi trong việc quản lý trạng thái hiển thị (UI State). Tuy nhiên, với các tác vụ bất đồng bộ phức tạp như Debounce Search hay xử lý Stream dữ liệu, RxJS vẫn là lựa chọn số một. Hãy kết hợp cả hai bằng toSignal() để tận dụng ưu điểm của từng bên.
Tổng kết
Angular 18 không đơn thuần là một bản cập nhật định kỳ. Nó đánh dấu sự chuyển mình sang một kỷ nguyên lập trình hiện đại, tinh gọn và hiệu suất cao hơn. Việc làm chủ Standalone Components và Signals sẽ giúp bạn xây dựng những ứng dụng web chuyên nghiệp, dễ bảo trì và mang lại trải nghiệm người dùng tốt nhất. Chúc bạn thành công với những dự án Angular sắp tới!

