Phân tích hiệu suất đường dẫn hiển thị quan trọng

Ilya Grigorik
Ilya Grigorik

Ngày xuất bản: ngày 31 tháng 3 năm 2014

Việc xác định và giải quyết nút thắt cổ chai hiệu suất đường dẫn quan trọng đòi hỏi phải có kiến thức tốt về những sai lầm phổ biến. Phần hướng dẫn giúp bạn xác định các mẫu hiệu suất phổ biến sẽ giúp bạn tối ưu hoá các trang của mình.

Việc tối ưu hóa đường dẫn hiển thị quan trọng cho phép trình duyệt vẽ trang nhanh nhất có thể: các trang nhanh hơn chuyển thành mức độ tương tác cao hơn, nhiều trang được xem hơn và cải thiện lượt chuyển đổi. Để giảm thiểu thời gian khách truy cập xem màn hình trống, chúng ta cần tối ưu hoá tài nguyên nào được tải và thứ tự tải.

Để giúp minh hoạ quá trình này, hãy bắt đầu từ trường hợp đơn giản nhất có thể và dần dần xây dựng trang của chúng tôi để bao gồm các tài nguyên, kiểu và logic ứng dụng bổ sung. Trong quá trình này, chúng tôi sẽ tối ưu hoá từng trường hợp; chúng tôi cũng sẽ xác định được vấn đề có thể xảy ra.

Cho đến nay, chúng tôi chỉ tập trung vào những gì xảy ra trong trình duyệt sau khi tài nguyên (tệp CSS, JS hoặc HTML) có sẵn để xử lý. Chúng tôi đã bỏ qua thời gian cần thiết để tìm nạp tài nguyên từ bộ nhớ đệm hoặc từ mạng. Chúng ta sẽ giả định như sau:

  • Một lượt khứ hồi mạng (độ trễ truyền tải) đến máy chủ có chi phí là 100 mili giây.
  • Thời gian phản hồi của máy chủ là 100 mili giây đối với tài liệu HTML và 10 mili giây đối với tất cả tệp khác.

Trải nghiệm hello world

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <title>Critical Path: No Style</title>
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
  </body>
</html>

Thử nào

Bắt đầu với mã đánh dấu HTML cơ bản và một hình ảnh duy nhất; không có CSS hoặc JavaScript. Sau đó, hãy mở bảng điều khiển Mạng trong Công cụ của Chrome cho nhà phát triển và kiểm tra thác nước tài nguyên thu được:

CRP

Như dự kiến, tệp HTML mất khoảng 200 mili giây để tải xuống. Lưu ý rằng phần trong suốt của đường màu xanh dương biểu thị khoảng thời gian mà trình duyệt chờ trên mạng mà không nhận được byte phản hồi nào, trong khi phần liền mạch cho biết thời gian hoàn tất quá trình tải xuống sau khi nhận được byte phản hồi đầu tiên. Tệp tải xuống HTML có kích thước rất nhỏ (<4K), vì vậy tất cả những gì chúng ta cần là một lần trả về để tìm nạp tệp đầy đủ. Do đó, tài liệu HTML mất khoảng 200 mili giây để tìm nạp, trong đó một nửa thời gian chờ mạng và nửa còn lại chờ phản hồi của máy chủ.

Khi nội dung HTML có sẵn, trình duyệt sẽ phân tích cú pháp các byte, chuyển đổi các byte đó thành mã thông báo và tạo cây DOM. Lưu ý rằng DevTools báo cáo thời gian cho sự kiện DOMContentLoaded ở dưới cùng (216 mili giây) một cách thuận tiện, thời gian này cũng tương ứng với đường dọc màu xanh dương. Khoảng cách giữa phần cuối tải xuống HTML và đường dọc màu xanh dương (DOMContentLoaded) là thời gian trình duyệt cần để tạo cây DOM, trong trường hợp này là chỉ vài mili giây.

Lưu ý rằng "ảnh tuyệt đẹp" của chúng tôi không chặn sự kiện domContentLoaded. Hóa ra, chúng ta có thể tạo cây kết xuất và thậm chí vẽ trang mà không cần chờ từng thành phần trên trang: không phải tất cả tài nguyên đều quan trọng để phân phối lần vẽ đầu tiên nhanh chóng. Trên thực tế, khi nói về đường dẫn hiển thị quan trọng, chúng ta thường nói đến mã đánh dấu HTML, CSS và JavaScript. Hình ảnh không chặn quá trình kết xuất ban đầu của trang, mặc dù chúng ta cũng nên cố gắng vẽ hình ảnh càng sớm càng tốt.

Tuy nhiên, sự kiện load (còn gọi là onload) bị chặn trên hình ảnh: Công cụ cho nhà phát triển báo cáo sự kiện onload ở 335 mili giây. Hãy nhớ rằng sự kiện onload đánh dấu thời điểm tất cả tài nguyên mà trang yêu cầu đã được tải xuống và xử lý; tại thời điểm này, vòng quay tải có thể ngừng quay trong trình duyệt (đường dọc màu đỏ trong thác nước).

Thêm JavaScript và CSS vào danh sách kết hợp

Trang "Trải nghiệm Hello World" có vẻ cơ bản, nhưng thực tế có rất nhiều điều diễn ra. Trên thực tế, chúng tôi sẽ không chỉ cần HTML: rất có thể chúng tôi sẽ có một bảng định kiểu CSS và một hoặc nhiều tập lệnh để thêm một số hoạt động tương tác cho trang của chúng tôi. Thêm cả hai vào bản phối để xem điều gì sẽ xảy ra:

<!DOCTYPE html>
<html>
  <head>
    <title>Critical Path: Measure Script</title>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <link href="style.css" rel="stylesheet" />
  </head>
  <body onload="measureCRP()">
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
    <script src="timing.js"></script>
  </body>
</html>

Thử nào

Trước khi thêm JavaScript và CSS:

CRP DOM

Với JavaScript và CSS:

DOM, CSSOM, JS

Việc thêm các tệp CSS và JavaScript bên ngoài sẽ thêm hai yêu cầu bổ sung vào thác nước của chúng ta, tất cả các yêu cầu này đều được trình duyệt gửi đi cùng một lúc. Tuy nhiên, hãy lưu ý rằng hiện tại, sự khác biệt về thời gian giữa sự kiện domContentLoadedonload nhỏ hơn nhiều.

Điều gì đã xảy ra?

  • Không giống như ví dụ về HTML thuần tuý, chúng ta cũng cần tìm nạp và phân tích cú pháp tệp CSS để tạo CSSOM và chúng ta cần cả DOM và CSSOM để tạo cây kết xuất.
  • Vì trang này cũng chứa một trình phân tích cú pháp chặn tệp JavaScript, nên sự kiện domContentLoaded bị chặn cho đến khi tệp CSS được tải xuống và phân tích cú pháp: vì JavaScript có thể truy vấn CSSOM, nên chúng ta phải chặn tệp CSS cho đến khi tệp này tải xuống thì mới có thể thực thi JavaScript.

Nếu chúng ta thay thế tập lệnh bên ngoài bằng tập lệnh cùng dòng thì sao? Ngay cả khi tập lệnh được đưa trực tiếp vào trang, trình duyệt cũng không thể thực thi tập lệnh đó cho đến khi CSSOM được tạo. Tóm lại, JavaScript cùng dòng cũng chặn trình phân tích cú pháp.

Tuy nhiên, mặc dù chặn trên CSS, nhưng việc nội tuyến tập lệnh có giúp trang hiển thị nhanh hơn không? Hãy thử và xem điều gì sẽ xảy ra.

JavaScript bên ngoài:

DOM, CSSOM, JS

JavaScript nội tuyến:

DOM, CSSOM và JS nội tuyến

Chúng tôi đang thực hiện ít hơn một yêu cầu, nhưng cả onload và thời gian domContentLoaded của chúng tôi đều hiệu quả như nhau. Tại sao? Chúng ta biết rằng không quan trọng JavaScript có nội tuyến hay bên ngoài, vì ngay khi trình duyệt gặp thẻ tập lệnh, trình duyệt sẽ chặn và đợi cho đến khi CSSOM được tạo. Ngoài ra, trong ví dụ đầu tiên của chúng tôi, trình duyệt tải xuống song song cả CSS và JavaScript và hoàn tất việc tải xuống gần như cùng một lúc. Trong trường hợp này, việc chèn mã JavaScript không giúp ích nhiều cho chúng tôi. Tuy nhiên, có một số chiến lược có thể giúp trang của chúng tôi hiển thị nhanh hơn.

Trước tiên, hãy nhớ rằng tất cả các tập lệnh cùng dòng đều là tính năng chặn trình phân tích cú pháp. Tuy nhiên, đối với các tập lệnh bên ngoài, chúng ta có thể thêm thuộc tính async để bỏ chặn trình phân tích cú pháp. Huỷ nội dung dòng lệnh của chúng tôi và thử:

<!DOCTYPE html>
<html>
  <head>
    <title>Critical Path: Measure Async</title>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <link href="style.css" rel="stylesheet" />
  </head>
  <body onload="measureCRP()">
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
    <script async src="timing.js"></script>
  </body>
</html>

Thử nào

JavaScript chặn trình phân tích cú pháp (ngoại):

DOM, CSSOM, JS

JavaScript không đồng bộ (bên ngoài):

DOM, CSSOM, JS không đồng bộ

Tốt hơn nhiều! Sự kiện domContentLoaded sẽ kích hoạt ngay sau khi HTML được phân tích cú pháp; trình duyệt biết không chặn JavaScript và vì không có tập lệnh chặn trình phân tích cú pháp nào khác nên quá trình xây dựng CSSOM cũng có thể tiến hành song song.

Ngoài ra, chúng ta có thể đưa cả CSS và JavaScript vào cùng một dòng:

<!DOCTYPE html>
<html>
  <head>
    <title>Critical Path: Measure Inlined</title>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <style>
      p {
        font-weight: bold;
      }
      span {
        color: red;
      }
      p span {
        display: none;
      }
      img {
        float: right;
      }
    </style>
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
    <script>
      var span = document.getElementsByTagName('span')[0];
      span.textContent = 'interactive'; // change DOM text content
      span.style.display = 'inline'; // change CSSOM property
      // create a new element, style it, and append it to the DOM
      var loadTime = document.createElement('div');
      loadTime.textContent = 'You loaded this page on: ' + new Date();
      loadTime.style.color = 'blue';
      document.body.appendChild(loadTime);
    </script>
  </body>
</html>

Thử nào

DOM, CSS cùng dòng, JS cùng dòng

Lưu ý thời gian domContentLoaded thực tế giống như trong ví dụ trước; thay vì đánh dấu JavaScript là không đồng bộ, chúng tôi đã đưa cả CSS và JS vào chính trang đó. Điều này làm cho trang HTML của chúng ta lớn hơn nhiều, nhưng điểm tích cực là trình duyệt không phải đợi để tìm nạp bất kỳ tài nguyên bên ngoài nào; mọi thứ đều có trong trang.

Như bạn có thể thấy, ngay cả với một trang rất cơ bản, việc tối ưu hoá đường dẫn kết xuất quan trọng cũng không phải là một bài tập đơn giản: chúng ta cần hiểu biểu đồ phần phụ thuộc giữa các tài nguyên khác nhau, chúng ta cần xác định những tài nguyên nào là "quan trọng" và chúng ta phải chọn trong số nhiều chiến lược về cách đưa các tài nguyên đó vào trang. Không có giải pháp chung cho vấn đề này; mỗi trang đều khác nhau. Bạn cần tự mình thực hiện theo một quy trình tương tự để tìm ra chiến lược tối ưu.

Tuy nhiên, hãy xem liệu chúng ta có thể lùi lại một chút để xác định một số mẫu hiệu suất chung hay không.

Mẫu hiệu suất

Trang đơn giản nhất có thể chỉ bao gồm đánh dấu HTML; không CSS, không JavaScript hoặc các loại tài nguyên khác. Để hiển thị trang này, trình duyệt phải bắt đầu yêu cầu, đợi tài liệu HTML đến, phân tích cú pháp tài liệu đó, tạo DOM và cuối cùng hiển thị tài liệu đó trên màn hình:

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <title>Critical Path: No Style</title>
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
  </body>
</html>

Thử nào

Xin chào thế giới CRP

Thời gian từ T0 đến T1 ghi lại thời gian xử lý của mạng và máy chủ. Trong trường hợp tốt nhất (nếu tệp HTML có kích thước nhỏ), chỉ cần một lượt khứ hồi mạng sẽ tìm nạp toàn bộ tài liệu. Do cách thức hoạt động của giao thức truyền tải TCP, các tệp lớn hơn có thể yêu cầu nhiều lượt đi và về hơn. Do đó, trong trường hợp tốt nhất, trang ở trên có một đường dẫn hiển thị quan trọng là một lượt khứ hồi (tối thiểu).

Bây giờ, hãy xem xét cùng một trang, nhưng với tệp CSS bên ngoài:

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <link href="style.css" rel="stylesheet" />
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
  </body>
</html>

Thử nào

DOM + CSSOM CRP

Một lần nữa, chúng tôi phải chịu trả về mạng để tìm nạp tài liệu HTML. Sau đó, mã đánh dấu được truy xuất cho chúng tôi biết rằng chúng tôi cũng cần tệp CSS; điều này có nghĩa là trình duyệt phải quay lại máy chủ và tải CSS trước khi có thể hiển thị trang trên màn hình. Do đó, trang này phải chịu tối thiểu hai lần trả về trước khi có thể hiển thị. Một lần nữa, tệp CSS có thể thực hiện nhiều giá trị trọn vòng, do đó nhấn mạnh vào "tối thiểu".

Dưới đây là một số thuật ngữ chúng tôi sử dụng để mô tả đường dẫn kết xuất quan trọng:

  • Tài nguyên quan trọng: Tài nguyên có thể chặn quá trình kết xuất ban đầu của trang.
  • Độ dài đường dẫn tới hạn: Số lượt khứ hồi hoặc tổng thời gian cần thiết để tìm nạp tất cả tài nguyên quan trọng.
  • Số byte quan trọng: Tổng số byte cần thiết để hiển thị trang đầu tiên, là tổng kích thước tệp được chuyển của tất cả tài nguyên quan trọng. Ví dụ đầu tiên của chúng ta, với một trang HTML duy nhất, chứa một tài nguyên quan trọng duy nhất (tài liệu HTML); chiều dài đường dẫn quan trọng cũng bằng một lượt truy cập mạng (giả sử tệp có kích thước nhỏ) và tổng số byte quan trọng chỉ là kích thước truyền của chính tài liệu HTML.

Bây giờ, hãy so sánh với các đặc điểm của đường dẫn quan trọng trong ví dụ trước về HTML và CSS:

DOM + CRP CSSOM

  • 2 tài nguyên quan trọng
  • 2 lượt khứ hồi trở lên cho độ dài đường dẫn tới hạn tối thiểu
  • 9 KB byte quan trọng

Chúng ta cần cả HTML và CSS để tạo cây hiển thị. Do đó, cả HTML và CSS đều là tài nguyên quan trọng: CSS chỉ được tìm nạp sau khi trình duyệt nhận được tài liệu HTML, do đó, chiều dài đường dẫn quan trọng tối thiểu là hai lượt đi và về. Cả hai tài nguyên có tổng cộng tối đa là 9KB byte quan trọng.

Bây giờ, hãy thêm một tệp JavaScript bổ sung vào danh sách kết hợp.

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <link href="style.css" rel="stylesheet" />
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
    <script src="app.js"></script>
  </body>
</html>

Thử nào

Chúng tôi đã thêm app.js, đây vừa là một thành phần JavaScript bên ngoài trên trang vừa là một tài nguyên chặn trình phân tích cú pháp (tức là tài nguyên quan trọng). Tệ hơn, để thực thi tệp JavaScript, chúng ta phải chặn và đợi CSSOM; hãy nhớ rằng JavaScript có thể truy vấn CSSOM và do đó, trình duyệt sẽ tạm dừng cho đến khi tải style.css xuống và tạo CSSOM.

DOM, CSSOM, CRP JavaScript

Tuy nhiên, trong thực tế, nếu chúng ta xem xét "thác nước mạng" của trang này, bạn sẽ thấy rằng cả yêu cầu CSS và JavaScript được bắt đầu cùng một lúc; trình duyệt sẽ nhận HTML, khám phá cả hai tài nguyên và khởi tạo cả hai yêu cầu. Do đó, trang hiển thị trong hình ảnh trước có các đặc điểm sau về đường dẫn quan trọng:

  • 3 tài nguyên quan trọng
  • 2 lượt đi và về trở lên cho chiều dài đường dẫn quan trọng tối thiểu
  • 11 KB byte quan trọng

Chúng ta hiện có 3 tài nguyên quan trọng tổng cộng tối đa 11KB byte quan trọng, nhưng độ dài đường dẫn quan trọng của chúng ta vẫn là hai lượt khứ hồi vì chúng ta có thể chuyển song song CSS và JavaScript. Việc tìm hiểu các đặc điểm của đường dẫn kết xuất quan trọng có nghĩa là bạn có thể xác định các tài nguyên quan trọng và cũng hiểu cách trình duyệt lên lịch tìm nạp các tài nguyên đó.

Sau khi trò chuyện với các nhà phát triển trang web, chúng tôi nhận thấy rằng JavaScript mà chúng tôi đưa vào trang không cần phải chặn; chúng tôi có một số số liệu phân tích và mã khác trong đó không cần chặn việc hiển thị trang. Với kiến thức đó, chúng ta có thể thêm thuộc tính async vào phần tử <script> để bỏ chặn trình phân tích cú pháp:

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <link href="style.css" rel="stylesheet" />
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
    <script src="app.js" async></script>
  </body>
</html>

Thử nào

DOM, CSSOM, CRP JavaScript không đồng bộ

Tập lệnh không đồng bộ có một số ưu điểm:

  • Tập lệnh không còn chặn trình phân tích cú pháp nữa và không phải là một phần của đường dẫn hiển thị quan trọng.
  • Vì không có các tập lệnh quan trọng khác nên CSS không cần chặn sự kiện domContentLoaded.
  • Sự kiện domContentLoaded kích hoạt càng sớm thì logic ứng dụng khác càng sớm có thể bắt đầu thực thi.

Do đó, trang được tối ưu hoá của chúng tôi hiện đã sử dụng lại hai tài nguyên quan trọng (HTML và CSS), với độ dài đường dẫn quan trọng tối thiểu là hai lượt khứ hồi và tổng cộng 9KB byte quan trọng.

Cuối cùng, nếu chỉ cần trang tính kiểu CSS để in, thì trang tính đó sẽ trông như thế nào?

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <link href="style.css" rel="stylesheet" media="print" />
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
    <script src="app.js" async></script>
  </body>
</html>

Thử nào

DOM, CSS không chặn và CRP JavaScript không đồng bộ

Vì tài nguyên style.css chỉ dùng để in nên trình duyệt không cần chặn tài nguyên này để hiển thị trang. Do đó, ngay khi quá trình tạo DOM hoàn tất, trình duyệt sẽ có đủ thông tin để hiển thị trang. Kết quả là trang này chỉ có một tài nguyên quan trọng (tài liệu HTML) và độ dài đường dẫn hiển thị quan trọng tối thiểu là một lượt khứ hồi.

Phản hồi