1. Ví dụ về cập nhật UI khi sử dụng JavaScript thuần
<html>
<div id="dataDiv"></div>
<button id="btn" (click)="handler()">ClickMe</button>
<script>
let count = 0;
// initial rendering
detectChange();
function renderHTML() {
document.getElementById('dataDiv').innerText = count;
}
function detectChange() {
const currentValue = document.getElementById('dataDiv').innerText;
if (currentValue !== count) {
renderHTML();
}
}
// update data inside button click event handler
document.getElementById('btn').addEventListener('click', () => {
// update count
count++;
// call detectChange manually
detectChange();
});
</script>
</html>
2. Change detection là gì?
Change detection là một cơ chế chịu trách nhiệm kiểm tra và cập nhật (re-render) View khi phát hiện thay đổi trong ứng dụng Angular để đồng bộ dữ liệu giữa component và View.
Theo mặc định, quá trình change detection luôn bắt đầu từ root component (thường là AppComponent) đến tất cả các component con cháu của nó trong toàn bộ ứng dụng. Do đó, việc kích hoạt change detection tại một component bất kỳ sẽ dẫn đến kích hoạt change detection ở tất cả component trong ứng dụng.
Chú ý:
- Change detection sẽ tự động cập nhật UI ngay khi phát hiện thay đổi và chỉ render lại phần UI nào liên quan đến model (property trong component class mà được binding với template) bị thay đổi chứ render lại toàn bộ UI của component.
- Quá trình kiểm tra các thay đổi của change detection cho các component là quá trình kiểm tra sự thay đổi của các property được binding giữa component và template (đây là những ràng buộc giữa view và model - tức khi model update thì phần nào trên view cần phải cập nhật lại). Do đó, khi change detection kiểm tra, nó chỉ quan tâm đến các property (trong component class) mà được binding với template chứ không quan tâm đến tổng thể những phần khác.
3. Change detection được kích hoạt khi nào?
- Events - các sự kiện tương tác của người dùng lên component View như click, change, input, submit,...
- Http Requests - E.g. Khi lấy dữ liệu từ backend server
- Timers - setTimeout(), setInterval(),...
- Các tác vụ bất đồng bộ không được xử lý bởi JavaScript mà được xử lý bởi Web APIs. Do đó mà JavaScript có thể tiếp tục thực khi các tác vụ đồng bộ khác trên Call Stack, trong khi Web APIs xử lý các tác vụ bất đồng bộ.
- Sau khi Web APIs xử lý xong, hàm callback của tác vụ bất đồng bộ được đẩy vào Callback Queue. Và các callback này chỉ được Event Loop đưa vào Call Stack để thực thi khi Call Stack trống. Do đó, chúng ta không biết được khi nào một tác vụ bất đồng bộ thực thi xong.
Zone.js
// this is the new version of addEventListener
function addEventListener(eventName, callback) {
// call the real addEventListener
callRealAddEventListener(eventName, function() {
// first call the original callback
callback(...);
// and then run Angular-specific functionality
let changed = angular.runChangeDetection();
if (changed) {
angular.reRenderUIPart();
}
});
}
Ví dụ, khi chúng ta gọi hàm setTimeout(), chúng ta thực sự gọi Zone.setTimeout() từ đó tạo ra một vùng mới bằng cách sử dụng zone.fork() trong đó trình xử lý đã cho được thực thi.
Do đó, Zone có thể theo dõi được khi một hoạt động bất đồng bộ được thực thi và khi nào hoạt động đó kết thúc. Một số phương thức mà zone.js ghi đè theo mặc định và cung cấp cho chúng tôi dưới dạng hook:
- Zone.setTimeout()
- Zone.setInterval()
- Zone.alert()
- Zone.prompt()
- Zone.requestAnimationFrame()
- Zone.addEventListener()
- Zone.removeEventListener()
Nhược điểm của Zone.js
NgZone
Từ tài liệu chính thức của Angular: “ Trong khi Zone.js có thể giám sát tất cả các trạng thái của hoạt động đồng bộ và không đồng bộ, Angular cũng cung cấp thêm một dịch vụ gọi là NgZone. Dịch vụ này tạo một khu vực được đặt tên angular để tự động kích hoạt phát hiện thay đổi. ”
NgZone kế thừa các API của zone.js và đồnng thời thêm một số event như onTurnStart(), onTurnDone(), onEventDone().
Lý do chính khiến Angular thêm các trình phát sự kiện của riêng nó thay vì dựa vào các lệnh gọi lại (callbacks) beforeTask và afterTask, là nó phải theo dõi bộ hẹn giờ (timers) và các tác vụ vi mô khác (micro tasks). Thật tuyệt khi các Observables được sử dụng như một API để xử lý các sự kiện này.
Khi ứng dụng Angular khởi chạy, NgZone sẽ tạo ra một zone và đặt tên là angular. angular zone sẽ override các API gốc của Web APIs như setTimeout(), setInterval(),... để giám sát các hoạt động bất đồng bộ. Khi có một sự kiện bất đồng bộ xảy ra như click button, angular zone sẽ phát ra một event onTurnStart và khi sự kiện hoàn thành, nó sẽ phát ra event onTurnDone để tự động kích hoạt Change Detection.
Các chiến lược Change Detection và tối ưu hiệu suất của ứng dụng Angular
Tài liệu tham khảo
- Understanding Zones
- Zones in Angular
- Change detection trong Angular và React
- Cách cải thiện hiệu suất của ứng dụng Angular với tính năng Phát hiện thay đổi và NgZone
- zone.js - Github
- Hiểu chiến lược OnPush để cải thiện hiệu suất Angular
- 10 điều mà mọi nhà phát triển Angular nên biết về Zone.js
- NgZone - angular.io