× Giới thiệu Lịch khai giảng Tin tức Sản phẩm học viên

React Reconciliation - Những điều bạn chưa biết về Reconciliation trong React

11/03/2022 04:55

React cung cấp một API khai báo để bạn không phải lo lắng về những thay đổi chính xác trên mỗi bản cập nhật. Điều này làm cho việc viết ứng dụng dễ dàng hơn rất nhiều, nhưng có thể không rõ cách thực hiện điều này trong React. Bài viết này giải thích các lựa chọn mà chúng tôi đã thực hiện trong thuật toán "khác biệt" của React để các bản cập nhật thành phần có thể dự đoán được trong khi vẫn đủ nhanh cho các ứng dụng hiệu suất cao.

React Reconciliation

Reconciliation trong React - Khái niệm

Khi bạn sử dụng React, tại một thời điểm nào đó, bạn có thể nghĩ rằng render()chức năng này giống như việc tạo một cây các phần tử React. Trong lần cập nhật trạng thái hoặc đạo cụ tiếp theo, render()hàm đó sẽ trả về một cây các phần tử React khác. Sau đó, React cần tìm ra cách cập nhật giao diện người dùng một cách hiệu quả để phù hợp với cây gần đây nhất.

Có một số giải pháp chung cho vấn đề thuật toán này là tạo ra số lượng phép toán tối thiểu để biến đổi một cây này thành một cây khác. Tuy nhiên, các thuật toán hiện đại có độ phức tạp theo thứ tự là O (n 3 ) trong đó n là số phần tử trong cây.

Nếu chúng tôi sử dụng điều này trong React, việc hiển thị 1000 phần tử sẽ yêu cầu theo thứ tự của một tỷ phép so sánh. Điều này là quá đắt. Thay vào đó, React triển khai thuật toán heuristic O (n) dựa trên hai giả định:

  1. Hai yếu tố của các loại khác nhau sẽ tạo ra các cây khác nhau.
  2. Nhà phát triển có thể gợi ý tại đó các phần tử con có thể ổn định trên các kết xuất khác nhau bằng một giá keyđỡ.

Trong thực tế, những giả định này có giá trị đối với hầu hết các trường hợp sử dụng thực tế.

Thuật toán khác biệt

Khi khác biệt hai cây, React đầu tiên sẽ so sánh hai phần tử gốc. Hành vi khác nhau tùy thuộc vào loại của các phần tử gốc.

Các yếu tố của các loại khác nhau

Bất cứ khi nào các phần tử gốc có nhiều loại khác nhau, React sẽ phá bỏ cây cũ và xây dựng cây mới từ đầu. Đi từ <a>tới <img>, hoặc từ <Article>tới <Comment>, hoặc từ <Button>tới <div>- bất kỳ điều nào trong số đó sẽ dẫn đến việc xây dựng lại toàn bộ.

Khi phá bỏ một cái cây, các nút DOM cũ sẽ bị phá hủy. Các thể hiện thành phần nhận được componentWillUnmount(). Khi xây dựng một cây mới, các nút DOM mới sẽ được chèn vào DOM. Các cá thể thành phần nhận UNSAFE_componentWillMount()và sau đó componentDidMount(). Trạng thái nào gắn với cây cổ thụ đều bị mất.

Bất kỳ thành phần nào bên dưới thư mục gốc cũng sẽ bị ngắt kết nối và trạng thái của chúng bị phá hủy. Ví dụ, khi khác nhau:

<div>

  <Counter />

</div>




<span>

  <Counter />

</span>

Điều này sẽ phá hủy cái cũ Countervà làm lại cái mới.

Phần tử DOM cùng loại

Khi so sánh hai phần tử React DOM cùng loại, React xem xét các thuộc tính của cả hai, giữ cùng một nút DOM cơ bản và chỉ cập nhật các thuộc tính đã thay đổi. Ví dụ:

<div className="before" title="stuff" />




<div className="after" title="stuff" />

Bằng cách so sánh hai yếu tố này, React biết chỉ sửa đổi classNametrên nút DOM bên dưới.

Khi cập nhật style, React cũng chỉ biết cập nhật các thuộc tính đã thay đổi. Ví dụ:

<div style={{color: 'red', fontWeight: 'bold'}} />




<div style={{color: 'green', fontWeight: 'bold'}} />

Khi chuyển đổi giữa hai phần tử này, React biết chỉ sửa đổi colorkiểu chứ không phải fontWeight.

Sau khi xử lý nút DOM, React sau đó sẽ đệ quy lại trên các phần tử con.

Các Yếu Tố Thành Phần Cùng Loại

Khi một thành phần cập nhật, phiên bản vẫn giữ nguyên, do đó trạng thái đó được duy trì qua các lần hiển thị. React cập nhật các đạo cụ của cá thể thành phần cơ bản để khớp với phần tử mới và các lệnh gọi UNSAFE_componentWillReceiveProps()cũng UNSAFE_componentWillUpdate()như componentDidUpdate()trên thể hiện cơ bản.

Tiếp theo, render()phương thức được gọi và thuật toán khác biệt lặp lại trên kết quả trước đó và kết quả mới.

Ghi chú:

Các phương pháp này được coi là kế thừa và bạn nên tránh chúng trong mã mới:

  • UNSAFE_componentWillUpdate()
  • UNSAFE_componentWillReceiveProps()

Recursion trên lớp con

Theo mặc định, khi đệ quy trên các nút con của một nút DOM, React chỉ lặp lại trên cả hai danh sách nút con cùng một lúc và tạo ra đột biến bất cứ khi nào có sự khác biệt.

Ví dụ: khi thêm một phần tử vào cuối phần con, việc chuyển đổi giữa hai cây này hoạt động tốt:

<ul>

  <li>first</li>

  <li>second</li>

</ul>




<ul>

  <li>first</li>

  <li>second</li>

  <li>third</li>

</ul>

React sẽ so khớp hai <li>first</li>cây, khớp hai <li>second</li>cây và sau đó chèn <li>third</li>cây.

Nếu bạn triển khai nó một cách ngây thơ, việc chèn một phần tử vào đầu sẽ có hiệu suất kém hơn. Ví dụ: chuyển đổi giữa hai cây này hoạt động kém:

<ul>

  <li>Duke</li>

  <li>Villanova</li>

</ul>




<ul>

  <li>Connecticut</li>

  <li>Duke</li>

  <li>Villanova</li>

</ul>

React sẽ biến đổi mọi con thay vì nhận ra rằng nó có thể giữ nguyên các cây con <li>Duke</li>và cây con <li>Villanova</li>. Sự kém hiệu quả này có thể là một vấn đề.

Chìa khóa

Để giải quyết vấn đề này, React hỗ trợ một keythuộc tính. Khi trẻ em có chìa khóa, React sử dụng chìa khóa để ghép những đứa trẻ ở cây ban đầu với những đứa trẻ ở cây tiếp theo. Ví dụ: thêm một keyvào ví dụ kém hiệu quả của chúng tôi ở trên có thể làm cho việc chuyển đổi cây trở nên hiệu quả:

<ul>

  <li key="2015">Duke</li>

  <li key="2016">Villanova</li>

</ul>




<ul>

  <li key="2014">Connecticut</li>

  <li key="2015">Duke</li>

  <li key="2016">Villanova</li>

</ul>

Bây giờ React biết rằng phần tử có khóa '2014'là phần tử mới và các phần tử có khóa '2015''2016'vừa mới di chuyển.

Trong thực tế, việc tìm chìa khóa thường không khó. Phần tử bạn sắp hiển thị có thể đã có một ID duy nhất, vì vậy, khóa có thể đến từ dữ liệu của bạn:

<li key={item.id}>{item.name}</li>

Khi không phải như vậy, bạn có thể thêm thuộc tính ID mới vào mô hình của mình hoặc băm một số phần nội dung để tạo khóa. Chìa khóa chỉ phải là duy nhất trong số các anh chị em của nó, không phải duy nhất trên toàn cầu.

Phương án cuối cùng, bạn có thể chuyển chỉ mục của một mục trong mảng làm khóa. Điều này có thể hoạt động tốt nếu các mục không bao giờ được sắp xếp lại, nhưng việc sắp xếp lại sẽ rất chậm.

Việc sắp xếp lại cũng có thể gây ra sự cố với trạng thái thành phần khi các chỉ mục được sử dụng làm khóa. Các phiên bản thành phần được cập nhật và sử dụng lại dựa trên khóa của chúng. Nếu khóa là một chỉ mục, việc di chuyển một mục sẽ thay đổi nó. Do đó, trạng thái thành phần cho những thứ như đầu vào không được kiểm soát có thể bị trộn lẫn và cập nhật theo những cách không mong muốn.

Trên đây là một số thông tin về Reconciliation trong React, bạn có thể tham khảo thêm để ứng dụng vào các dự án sắp tới của mình! Bạn cũng có thể tìm hiểu thêm thông tin về khóa học ReactJS hoặc các khóa học lập trình khác của Viện công nghệ thông tin T3H.