Change detection trong Angular

Change detection trong Angular

{tocify} $title={Table of Content}

1. Ví dụ về cập nhật UI khi sử dụng JavaScript thuần

Trước khi tìm hiểu về change detection trong Angular, chúng ta hãy cùng xem qua ví dụ sau đây để hiểu về cách mà UI được cập nhật theo cách làm thủ công:
<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>
Trong ví dụ trên, ta thấy rằng, sau khi chúng ta cập nhật dữ liệu cho biến count, chúng ta cần gọi detectChange() theo cách thủ công để kiểm tra xem dữ liệu đã thay đổi hay chưa. Nếu dữ liệu thay đổi, chúng ta sẽ render lại HTML để phản ánh dữ liệu đã được cập nhật.
Trong Angular, chúng ta sẽ không cần thực hiện kiểm tra sự thay đổi của dữ liệu theo cách trên mà cơ chế Change detection của Angular sẽ tự động làm việc này giúp cho chúng ta. Bất cứ khi nào chúng ta cập nhật dữ liệu, HTML sẽ được cập nhật tự động.

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ú ý:

  1. 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.
  2. 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?

Change detection được kích hoạt mỗi khi có sự kiện làm thay đổi dữ liệu được binding giữa component và template xảy ra. Và trong Angular, các property binding của component có thể bị thay đổi bởi 3 thứ:
  1. 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,...
  2. Http Requests - E.g. Khi lấy dữ liệu từ backend server
  3. Timers - setTimeout(), setInterval(),...
Khi các sự kiện kể trên diễn ra và hoàn thành thì Change Detection sẽ được gọi. Tuy nhiên, nếu để ý, chúng ta sẽ thấy 3 thứ gây sự thay đổi được kể ở trên là "Tất cả chúng đều bất đồng bộ (asynchronous).".
Điều này khá quan trọng, bởi vì về bản chất, Change detection được gọi ngay khi tác vụ bất đồng bộ được thực thi xong. Nhưng nếu hiểu rõ về cơ chế hoạt động của JavaScript thì chúng ta biết rằng:
  • 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.
Vậy làm cách nào mà Angular biết được khi nào một tác vụ bất đồng bộ hoàn thành để gọi Change Detection?
Angular biết được thời điểm cần gọi Change Detection nhờ vào NgZone service - một service được xây dựng dựa trên thư viện Zone.js.

Trước khi tìm hiểu Zone.jsNgZone, chúng ta có một khái niệm cần biết, đó là Execution Context (Bối cảnh thực thi):
Trong JavaScript, Execution Context được tạo ra khi ứng dụng khởi chạy và bất cứ khi nào một function được thực thi. Execution Context chứa thông tin về môi trường bên trong code hiện tại đang được thực thi như các biến, scope chain,...

Zone.js

Zone cung cấp một execution context tồn tại qua các tác vụ bất đồng bộ. Sau khi hoàn tất hoạt động không đồng bộ, hàm callback được thực thi trong cùng một vùng mà nó đã được đăng ký.
Hay ta có thể nói rằng, Zone là một cơ chế để ngăn chặn và theo dõi các hoạt động không đồng bộ.
Ngay sau khi chúng ta nhúng zone.js vào trang web của chúng ta, Zone.js sẽ ghi đè tất cả các hàm và phương thức không đồng bộ nguyên bản để chạy trong một zone mới.
// 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

Zone.js thay đổi hành vi tiêu chuẩn của API trình duyệt. Mặc dù thực tế là Monkey Patch được thực hiện gọn gàng, nhưng sẽ có thêm chi phí về thời gian khi gọi các hàm cơ sở. Mặc dù những chi phí này là nhỏ, nhưng chúng rất đáng chú ý đối với các ứng dụng lớn.

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) beforeTaskafterTask, 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

Đang cập nhật...



Tài liệu tham khảo


Đăng nhận xét

Mới hơn Cũ hơn