Tìm hiểu về tối ưu render trên trình duyệt (phần 2): DOM & CSS

Ở bài viết trước chúng ta đã tìm hiểu về khía niệm độ mượt của ứng dụng web và một yếu tố ảnh hưởng lớn đến độ mượt, đó là tác vụ reflow. Trong bài viết này chúng ta sẽ cùng tìm hiểu các khía cạnh khác ảnh hưởng đến độ mượt của ứng dụng web nói chung và ảnh hưởng liên đới đến reflow nói riêng, cụ thể là Tối ưu độ sâu DOMtối ưu sử dụng CSS selector (Style trong rendering pipeline).

Điều cần chú ý:

Tất cả các hành động dính dáng đến style thay đổi DOM bằng việc thêm, xoá phần tử, thay đổi thuộc tính, class hoặc animation sẽ đều khiến trình duyệt tính toán lại style và (trong nhiều trường hợp) tính toán lại layout (chính là hành vi reflow đã đề cập trong bài viết trước).

Độ sâu của DOM (DOM Depth)

Độ sâu DOM ảnh hưởng như thế nào?

Độ sâu của DOM ảnh hưởng đến nhiều đối tượng như server, network, client run time, client memory. Tuy nhiên (tất nhiên) ở đây chúng ta sẽ chỉ đề cập đến client run time. Tại sao DOM quá sâu sẽ gây ảnh hưởng performance. Trong rất nhiều trường hợp, việc update một DOM node sẽ kéo theo update tất cả các DOM node con, đồng nghĩa việc việc tất cả các DOM bị update liên đới sẽ phải được tính toán lại (reflow). Nếu DOM của chúng ta quá sâu, đồng nghĩa với việc việc tính toán sẽ trở nên phức tạp hơn.

Cây DOM như thế nào là phù hợp

Vậy sâu bao nhiêu thì phù hợp. Sau đây là các con số tham khảo từ Google về một case DOM hợp lý:

  • có <= 1500 nodes.
  • sâu tối đa 32 nodes.
  • Không có node cha nào có > 60 nodes con.

Một vài lời khuyên

  • Chỉ tạo DOM node khi cần và nên xoá đi khi không cần nữa.
  • Đối với các website server-side rendering, nếu server trả về cây DOM quá to, bạn có thể / nên đánh giá những DOM nào cần cho việc hiển thị và loại bỏ chúng khỏi cây DOM.
  • Lazy load DOM nodes cũng là một cách. Bạn có thế xoá DOM node khi chúng được cuộn ra ngoài view port và tạo DOM node khi chúng được cuộn vào viewport. Kĩ thuật này được gọi là virtual scroll.

CSS selector

Các giai đoạn tính toán style

  • Giai đoạn 1: trình duyệt tìm các tổ hợp selector - element thoả mãn các điều kiện selector đã viết (chính là việc tìm ra classes, pseudo-selectors và IDs tương ứng).
  • Giai đọạn 2: tính toán styles cuối cùng cho các element Theo như tính toán, việc tìm tổ hợp selector - element dựa theo các css selector rules chiếm đến 50% thời gian, 50% còn lại là việc tính toán RenderStyle.

Tối ưu như thế nào.

Giảm độ phức tạp của selector

Để giảm độ phức tạp của selector, cách đơn giản nhất là giảm độ phức tạp (độ sâu) của DOM. Nếu độ sâu của DOM đạt được con số tối ưu thì mặc định css selector cũng sẽ được tối ưu theo. Tuy nhiên, nếu chúng ta buộc phải làm việc với DOM sâu thì việc tối ưu selector là bắt buộc phải chú ý. Có một phương thức tổ chức CSS selector xoay quanh class name rất tốt cho việc tối ưu selector, đó chính là BEM.

Ví dụ, trong một số trường hợp, việc chạm tới phần tử cuối cùng sẽ trông như thế này

.box:nth-last-child(-n+1) .title {
  /* styles */
}

Thay vì dùng logic như trên, nếu đặt tên theo luật BEM, chúng ta có thể đặt:

.final-box-title {
  /* styles */
}

Cách đặt tên này sẽ giàm thiểu thời gian ghép cặp selector rất nhiều.

Việc sử dụng block naming rule của BEM cũng rất hữu ích. BEM khuyến nghị mỗi thằng nên có 1 class riêng. Và nếu cần target tới cấp bậc thấp hơn, hãy đặt tên dựa vào cấp bậc cha.

.list { }
.list__list-item { }

Giảm số lượng phần tử phải tính lại style.

Cái này thì có lẽ có thể bỏ qua với các trình duyệt hiện đại, vì chúng đang được tối ưu tốt cho việc phát hiện những phần tử nào thực sự cần tính toán lại dựa vào vị trí trong cây DOM. Nếu bạn muốn tìm hiểu thêm có thể đọc tại đây.

Chúng ta sẽ cùng tìm hiểu các yếu tố khác ảnh hưởng đến rendering performance trong phần tiếp theo.

Tham khảo: