Không chống lại trình duyệt tải trước trình quét

Tìm hiểu xem trình duyệt tải trước trình quét là gì, cách trình duyệt giúp cải thiện hiệu suất và tránh tình trạng này.

Một khía cạnh đáng bỏ qua của việc tối ưu hoá tốc độ trang liên quan đến việc biết một chút về bộ phận bên trong của trình duyệt. Các trình duyệt thực hiện một số hoạt động tối ưu hoá nhất định để cải thiện hiệu suất theo những cách mà nhà phát triển chúng tôi không làm được, nhưng chỉ miễn là những cách tối ưu hoá đó không vô tình bị cản trở.

Một cách tối ưu hoá nội bộ của trình duyệt cần hiểu là trình duyệt tải trước trình quét. Bài đăng này sẽ đề cập đến cách hoạt động của trình quét tải trước và quan trọng hơn là cách bạn có thể tránh ảnh hưởng đến công cụ này.

Trình quét tải trước là gì?

Mỗi trình duyệt đều có một trình phân tích cú pháp HTML chính để mã hoá mã đánh dấu thô và xử lý mã đánh dấu đó thành mô hình đối tượng. Mọi việc này sẽ diễn ra cho đến khi trình phân tích cú pháp tạm dừng khi tìm thấy một tài nguyên chặn, chẳng hạn như biểu định kiểu được tải bằng phần tử <link>, hoặc tập lệnh được tải bằng phần tử <script> mà không có thuộc tính async hoặc defer.

Sơ đồ trình phân tích cú pháp HTML.
Hình 1: Sơ đồ về cách chặn trình phân tích cú pháp HTML chính của trình duyệt. Trong trường hợp này, trình phân tích cú pháp sẽ chạy vào phần tử <link> cho một tệp CSS bên ngoài. Tệp này sẽ chặn trình duyệt phân tích cú pháp phần còn lại của tài liệu (hoặc thậm chí hiển thị bất kỳ nội dung nào trong số đó) cho đến khi CSS được tải xuống và phân tích cú pháp.

Trong trường hợp tệp CSS, cả quá trình phân tích cú pháp và hiển thị đều bị chặn để ngăn flash nội dung chưa được định kiểu (FOUC), đó là khi một phiên bản chưa được định kiểu của một trang có thể hiển thị trong thời gian ngắn trước khi áp dụng kiểu cho trang đó.

Trang chủ web.dev ở trạng thái chưa định kiểu (bên trái) và trạng thái đã tạo kiểu (bên phải).
Hình 2: Ví dụ mô phỏng về FOUC. Ở bên trái là trang chủ của web.dev không có kiểu. Ở bên phải là cùng một trang với các kiểu được áp dụng. Trạng thái chưa định kiểu có thể xảy ra trong nháy mắt nếu trình duyệt không chặn hiển thị trong khi biểu định kiểu đang được tải xuống và xử lý.

Trình duyệt cũng chặn phân tích cú pháp và hiển thị trang khi gặp các phần tử <script> không có thuộc tính defer hoặc async.

Lý do là vì trình duyệt không thể biết chắc liệu có tập lệnh cụ thể nào sẽ sửa đổi DOM trong khi trình phân tích cú pháp HTML chính vẫn đang hoạt động hay không. Đây là lý do tại sao phổ biến là tải JavaScript của bạn ở cuối tài liệu để ảnh hưởng của việc phân tích cú pháp và hiển thị bị chặn trở nên không đáng kể.

Đây là những lý do chính đáng cho lý do trình duyệt nên chặn cả phân tích cú pháp và hiển thị. Tuy nhiên, việc chặn một trong các bước quan trọng này là không mong muốn, vì chúng có thể trì hoãn chương trình bằng cách trì hoãn việc khám phá các tài nguyên quan trọng khác. Rất may, các trình duyệt sẽ cố gắng hết sức để giảm thiểu những vấn đề này thông qua một trình phân tích cú pháp HTML phụ được gọi là trình quét tải trước.

Sơ đồ của cả trình phân tích cú pháp HTML chính (bên trái) và trình quét tải trước (bên phải), là trình phân tích cú pháp HTML phụ.
Hình 3: Sơ đồ mô tả cách trình quét tải trước hoạt động song song với trình phân tích cú pháp HTML chính để tải tài sản theo suy đoán. Ở đây, trình phân tích cú pháp HTML chính bị chặn khi tải và xử lý CSS trước khi có thể bắt đầu xử lý mã đánh dấu hình ảnh trong phần tử <body>. Tuy nhiên, trình quét tải trước có thể xem trước trong mã đánh dấu thô để tìm tài nguyên hình ảnh đó và bắt đầu tải trước khi trình phân tích cú pháp HTML chính được bỏ chặn.

Vai trò của trình quét tải trước là có tính suy đoán, nghĩa là công cụ này sẽ kiểm tra mã đánh dấu thô để tìm tài nguyên có thể tìm nạp trước khi trình phân tích cú pháp HTML chính phát hiện ra chúng.

Cách nhận biết khi trình quét tải trước đang hoạt động

Trình quét tải trước tồn tại do bị chặn kết xuất và phân tích cú pháp. Nếu hai vấn đề hiệu suất này chưa từng tồn tại, thì trình quét tải trước sẽ không thật hữu ích. Chìa khoá để xác định xem trang web có được hưởng lợi từ trình quét tải trước hay không phụ thuộc vào các hiện tượng chặn này. Để làm việc đó, bạn có thể thêm một độ trễ nhân tạo cho các yêu cầu để tìm hiểu xem trình quét tải trước đang hoạt động ở đâu.

Hãy lấy trang này văn bản và hình ảnh cơ bản với biểu định kiểu làm ví dụ. Vì các tệp CSS chặn cả hiển thị và phân tích cú pháp, bạn đưa ra độ trễ giả tạo là 2 giây cho biểu định kiểu thông qua dịch vụ proxy. Độ trễ này giúp bạn dễ dàng xem trong thác nước mạng, nơi trình quét tải trước đang hoạt động.

Biểu đồ thác nước của mạng WebPageTest minh hoạ độ trễ giả tạo 2 giây áp dụng cho biểu định kiểu.
Hình 4: Biểu đồ dạng thác nước mạng WebPageTest về trang web chạy trong Chrome trên thiết bị di động qua kết nối 3G mô phỏng. Mặc dù biểu định kiểu bị trì hoãn một cách giả tạo qua proxy hai giây trước khi bắt đầu tải, hình ảnh nằm sau trong phần tải trọng đánh dấu vẫn được phát hiện bằng trình quét tải trước.

Như bạn có thể thấy trong thác nước, trình quét tải trước sẽ phát hiện phần tử <img> ngay cả khi tính năng kết xuất và phân tích cú pháp tài liệu bị chặn. Nếu không có tính năng tối ưu hóa này, trình duyệt sẽ không thể tìm nạp mọi thứ một cách có cơ hội trong thời gian chặn và nhiều yêu cầu tài nguyên hơn sẽ liên tiếp thay vì đồng thời.

Ngoài ví dụ về đồ chơi đó, hãy xem xét một số mẫu trong thế giới thực nơi có thể đánh bại trình quét tải trước — và có thể làm gì để khắc phục chúng.

Đã chèn async tập lệnh

Giả sử bạn có HTML trong <head> bao gồm một số JavaScript nội tuyến như sau:

<script>
  const scriptEl = document.createElement('script');
  scriptEl.src = '/yall.min.js';

  document.head.appendChild(scriptEl);
</script>

Theo mặc định, các tập lệnh được chèn có giá trị async. Vì vậy, khi được chèn vào, tập lệnh này sẽ hoạt động như thể thuộc tính async được áp dụng. Điều đó có nghĩa là quảng cáo sẽ chạy sớm nhất có thể và không chặn hiển thị. Nghe có vẻ tối ưu phải không? Tuy nhiên, nếu cho rằng <script> cùng dòng này xuất hiện sau phần tử <link> tải một tệp CSS bên ngoài, thì bạn sẽ nhận được kết quả dưới mức tối ưu:

Biểu đồ WebPageTest này cho thấy quá trình quét tải trước bị loại bỏ khi một tập lệnh được chèn.
Hình 5: Biểu đồ dạng thác nước của mạng WebPageTest về một trang web chạy trong Chrome trên thiết bị di động thông qua kết nối 3G mô phỏng. Trang này chứa một biểu định kiểu và một tập lệnh async được chèn. Trình quét tải trước không thể khám phá tập lệnh trong giai đoạn chặn hiển thị, vì tập lệnh này được chèn vào ứng dụng.

Hãy phân tích những gì đã xảy ra sau đây:

  1. Ở 0 giây, tài liệu chính được yêu cầu.
  2. Tại 1,4 giây, byte đầu tiên của yêu cầu điều hướng sẽ đến.
  3. Ở 2 giây, CSS và hình ảnh sẽ được yêu cầu.
  4. Do trình phân tích cú pháp bị chặn việc tải biểu định kiểu và JavaScript nội tuyến chèn tập lệnh async sẽ sau biểu định kiểu đó ở 2,6 giây, nên chức năng mà tập lệnh cung cấp sẽ không hoạt động ngay khi có thể.

Điều này chưa tối ưu vì yêu cầu đối với tập lệnh chỉ xảy ra sau khi tệp định kiểu đã hoàn tất việc tải xuống. Điều này khiến tập lệnh không chạy sớm nhất có thể. Ngược lại, vì bạn có thể phát hiện phần tử <img> trong mã đánh dấu do máy chủ cung cấp, nên trình quét tải trước sẽ phát hiện phần tử này.

Vậy điều gì sẽ xảy ra nếu bạn sử dụng thẻ <script> thông thường với thuộc tính async thay vì chèn tập lệnh vào DOM?

<script src="/yall.min.js" async></script>

Đây là kết quả:

Thác nước mạng WebPageTest mô tả cách một tập lệnh không đồng bộ được tải bằng cách sử dụng phần tử tập lệnh HTML vẫn có thể phát hiện được bằng trình quét tải trước trình duyệt, mặc dù trình phân tích cú pháp HTML chính của trình duyệt bị chặn trong khi tải xuống và xử lý biểu định kiểu.
Hình 6: Biểu đồ dạng thác nước của mạng WebPageTest về một trang web chạy trong Chrome trên thiết bị di động qua kết nối 3G mô phỏng. Trang này chứa một biểu định kiểu và một phần tử async <script>. Trình quét tải trước sẽ phát hiện tập lệnh trong giai đoạn chặn hiển thị và tải tập lệnh đó đồng thời với CSS.

Bạn có thể sẽ muốn gợi ý rằng các vấn đề này có thể được khắc phục bằng cách sử dụng rel=preload. Cách này chắc chắn sẽ hiệu quả, nhưng có thể dẫn đến một số tác dụng phụ. Suy cho cùng thì tại sao bạn nên sử dụng rel=preload để khắc phục vấn đề có thể tránh được bằng cách không chèn phần tử <script> vào DOM?

Thác nước WebPageTest cho thấy cách sử dụng gợi ý tài nguyên rel=foreground để thúc đẩy việc khám phá tập lệnh được chèn không đồng bộ, mặc dù theo cách có thể có các tác dụng phụ không mong muốn.
Hình 7: Biểu đồ dạng thác nước trên mạng WebPageTest về một trang web chạy trong Chrome trên thiết bị di động thông qua kết nối 3G mô phỏng. Trang này chứa một biểu định kiểu và tập lệnh async đã chèn, nhưng tập lệnh async được tải trước để đảm bảo phát hiện tập lệnh này sớm hơn.

Việc tải trước "khắc phục" vấn đề này ở đây, nhưng việc này gây ra một vấn đề mới: tập lệnh async trong hai bản minh hoạ đầu tiên (mặc dù được tải trong <head>) được tải ở mức độ ưu tiên "Thấp", trong khi biểu định kiểu được tải ở mức độ ưu tiên "Cao nhất". Trong bản minh hoạ cuối cùng khi tập lệnh async được tải trước, biểu định kiểu vẫn được tải ở mức độ ưu tiên "Cao nhất", nhưng mức độ ưu tiên của tập lệnh đã được nâng lên thành "Cao".

Khi mức độ ưu tiên của tài nguyên tăng lên, trình duyệt sẽ phân bổ thêm băng thông cho tài nguyên đó. Điều này có nghĩa là mặc dù biểu định kiểu có mức độ ưu tiên cao nhất, mức độ ưu tiên cao hơn của tập lệnh có thể gây ra tranh chấp băng thông. Đó có thể là một yếu tố làm cho kết nối chậm hoặc trong trường hợp tài nguyên khá lớn.

Câu trả lời ở đây rất đơn giản: nếu cần tập lệnh trong quá trình khởi động, đừng đánh bại trình quét tải trước bằng cách chèn tập lệnh đó vào DOM. Hãy thử nghiệm vị trí của phần tử <script>, cũng như các thuộc tính như deferasync nếu cần.

Tải từng phần bằng JavaScript

Tải từng phần là một phương pháp hữu hiệu để bảo toàn dữ liệu, thường áp dụng cho hình ảnh. Tuy nhiên, đôi khi, tính năng tải từng phần được áp dụng không chính xác cho các hình ảnh "trong màn hình đầu tiên".

Điều này dẫn đến các vấn đề tiềm ẩn về khả năng phát hiện tài nguyên khi quan tâm đến trình quét tải trước và có thể trì hoãn không cần thiết khoảng thời gian cần thiết để khám phá thông tin tham chiếu đến hình ảnh, tải xuống, giải mã và hiển thị hình ảnh đó. Hãy xem ví dụ về mã đánh dấu hình ảnh này:

<img data-src="/sand-wasp.jpg" alt="Sand Wasp" width="384" height="255">

Việc sử dụng tiền tố data- là một mẫu hình phổ biến trong các trình tải từng phần dựa trên JavaScript. Khi hình ảnh được cuộn vào khung nhìn, trình tải từng phần sẽ xoá tiền tố data-, nghĩa là trong ví dụ trước, data-src trở thành src. Thông tin cập nhật này sẽ nhắc trình duyệt tìm nạp tài nguyên.

Mẫu này sẽ không có vấn đề gì cho đến khi được áp dụng cho các hình ảnh trong khung nhìn khi khởi động. Vì trình quét tải trước không đọc thuộc tính data-src theo cách giống như thuộc tính src (hoặc srcset), nên không phát hiện được thông tin tham chiếu hình ảnh trước đó. Tệ hơn nữa, hình ảnh này sẽ bị trì hoãn quá trình tải cho đến sau khi trình tải từng phần JavaScript tải xuống, biên dịch và thực thi.

Biểu đồ thác nước trên mạng WebPageTest cho thấy mức độ trì hoãn của hình ảnh tải từng phần trong khung nhìn trong quá trình khởi động vì trình quét tải trước trình duyệt không thể tìm thấy tài nguyên hình ảnh và chỉ tải khi JavaScript cần thiết để tải từng phần hoạt động. Hình ảnh được phát hiện muộn hơn nhiều so với mong muốn.
Hình 8: Biểu đồ dạng thác nước của mạng WebPageTest về một trang web chạy trong Chrome trên thiết bị di động qua kết nối 3G mô phỏng. Tài nguyên hình ảnh được tải từng phần một cách không cần thiết, mặc dù tài nguyên này hiển thị trong khung nhìn trong quá trình khởi động. Thao tác này sẽ đánh bại trình quét tải trước và gây ra độ trễ không cần thiết.

Tuỳ thuộc vào kích thước của hình ảnh (có thể phụ thuộc vào kích thước của khung nhìn), nó có thể là phần tử ứng viên cho Thời gian hiển thị nội dung lớn nhất (LCP). Khi trình quét tải trước không thể tìm nạp trước tài nguyên hình ảnh theo suy đoán – có thể là trong thời điểm mà(các) biểu định kiểu của trang chặn hiển thị – LCP bị ảnh hưởng.

Giải pháp là thay đổi mã đánh dấu hình ảnh:

<img src="/sand-wasp.jpg" alt="Sand Wasp" width="384" height="255">

Đây là mẫu tối ưu cho hình ảnh trong khung nhìn khi khởi động, vì trình quét tải trước sẽ khám phá và tìm nạp tài nguyên hình ảnh nhanh hơn.

Biểu đồ dạng thác nước của mạng WebPageTest mô tả tình huống tải cho một hình ảnh trong khung nhìn khi khởi động. Hình ảnh không được tải từng phần, nghĩa là hình ảnh không phụ thuộc vào tập lệnh để tải, nghĩa là trình quét tải trước có thể phát hiện hình ảnh sớm hơn.
Hình 9: Biểu đồ dạng thác nước của mạng WebPageTest về một trang web chạy trong Chrome trên thiết bị di động thông qua kết nối 3G mô phỏng. Trình quét tải trước sẽ phát hiện tài nguyên hình ảnh trước khi CSS và JavaScript bắt đầu tải, giúp trình duyệt bắt đầu tải.

Kết quả trong ví dụ đơn giản này là cải thiện LCP trong 100 mili giây trên kết nối chậm. Có vẻ như đây không phải là một cải tiến lớn, nhưng đó là khi bạn cho rằng giải pháp này là một cách khắc phục nhanh đối với mã đánh dấu và hầu hết các trang web phức tạp hơn so với tập hợp ví dụ này. Điều đó có nghĩa là các ứng viên LCP có thể phải cạnh tranh về băng thông với nhiều tài nguyên khác, vì vậy, việc tối ưu hoá như thế này ngày càng trở nên quan trọng.

Hình nền CSS

Hãy nhớ rằng trình duyệt tải trước trình quét đánh dấu. Tính năng này không quét các loại tài nguyên khác, chẳng hạn như CSS (có thể liên quan đến các lượt tìm nạp hình ảnh mà thuộc tính background-image tham chiếu).

Giống như HTML, các trình duyệt xử lý CSS thành mô hình đối tượng riêng được gọi là CSSOM. Nếu các tài nguyên bên ngoài được phát hiện khi CSSOM được xây dựng, các tài nguyên đó được yêu cầu tại thời điểm khám phá chứ không phải bằng trình quét tải trước.

Giả sử ứng viên LCP của trang là một phần tử có thuộc tính CSS background-image. Sau đây là những gì sẽ xảy ra khi tài nguyên tải:

Biểu đồ thác nước của mạng WebPageTest mô tả một trang có đề xuất LCP được tải từ CSS bằng thuộc tính hình nền. Vì hình ảnh đề xuất LCP nằm trong loại tài nguyên mà trình quét tải trước không thể kiểm tra, tài nguyên bị trễ khi tải cho đến khi CSS được tải xuống và xử lý, làm chậm thời gian hiển thị của ứng viên LCP này.
Hình 10: Biểu đồ dạng thác nước của mạng WebPageTest về một trang web chạy trong Chrome trên thiết bị di động thông qua kết nối 3G mô phỏng. Đề xuất LCP của trang là một phần tử có thuộc tính background-image CSS (hàng 3). Hình ảnh mà hệ thống yêu cầu không bắt đầu tìm nạp cho đến khi trình phân tích cú pháp CSS tìm thấy.

Trong trường hợp này, trình quét tải trước không bị đánh bại nhiều vì không liên quan đến. Dù vậy, nếu đề xuất LCP trên trang là từ một tài sản CSS background-image, bạn vẫn nên tải trước hình ảnh đó:

<!-- Make sure this is in the <head> below any
     stylesheets, so as not to block them from loading -->
<link rel="preload" as="image" href="lcp-image.jpg">

Gợi ý rel=preload đó tuy nhỏ nhưng giúp trình duyệt phát hiện hình ảnh sớm hơn so với khả năng:

Biểu đồ thác nước của mạng WebPageTest hiển thị hình nền CSS (ứng cử viên LCP) tải sớm hơn nhiều do sử dụng gợi ý rel=advanced. Thời gian LCP tăng khoảng 250 mili giây.
Hình 11: Biểu đồ dạng thác nước của mạng WebPageTest về một trang web chạy trong Chrome trên thiết bị di động thông qua kết nối 3G mô phỏng. Đề xuất LCP của trang là một phần tử có thuộc tính background-image CSS (hàng 3). Gợi ý rel=preload giúp trình duyệt phát hiện hình ảnh sớm hơn khoảng 250 mili giây so với khi không có gợi ý.

Với gợi ý rel=preload, ứng viên LCP sẽ được phát hiện sớm hơn, nhờ đó giảm thời gian LCP. Mặc dù gợi ý đó giúp khắc phục vấn đề này, nhưng bạn nên đánh giá xem liệu đề xuất LCP cho hình ảnh của bạn được tải qua CSS hay không. Khi sử dụng thẻ <img>, bạn sẽ kiểm soát được nhiều hơn việc tải hình ảnh phù hợp với khung nhìn, đồng thời cho phép trình quét tải trước phát hiện hình ảnh đó.

Nội dung quá nhiều tài nguyên

Nội tuyến là phương pháp đặt tài nguyên bên trong HTML. Bạn có thể cùng dòng các biểu định kiểu trong phần tử <style>, tập lệnh trong phần tử <script> và hầu hết mọi tài nguyên khác bằng cách sử dụng phương thức mã hoá base64.

Tài nguyên cùng dòng có thể nhanh hơn tải xuống tài nguyên vì tài nguyên không được đưa ra yêu cầu riêng. URL này nằm ngay trong tài liệu và tải ngay lập tức. Tuy nhiên, có những hạn chế đáng kể:

  • Nếu bạn không lưu HTML của mình vào bộ nhớ đệm và cũng không thể lưu trữ HTML đó nếu phản hồi HTML là động – thì các tài nguyên cùng dòng không bao giờ được lưu vào bộ nhớ đệm. Điều này ảnh hưởng đến hiệu suất vì các tài nguyên cùng dòng không thể sử dụng lại.
  • Ngay cả khi bạn có thể lưu HTML vào bộ nhớ đệm, các tài nguyên cùng dòng vẫn không được chia sẻ giữa các tài liệu. Điều này làm giảm hiệu quả lưu vào bộ nhớ đệm so với các tệp bên ngoài có thể được lưu vào bộ nhớ đệm và sử dụng lại trên toàn bộ nguồn gốc.
  • Nếu bạn đặt nội tuyến quá nhiều, thì sẽ khiến trình quét tải trước không phát hiện được các tài nguyên trong tài liệu sau này, vì việc tải xuống nội dung bổ sung, cùng dòng, sẽ mất nhiều thời gian hơn.

Hãy lấy trang này làm ví dụ. Trong một số điều kiện nhất định, đề xuất LCP là hình ảnh ở đầu trang và CSS nằm trong một tệp riêng biệt được tải bằng phần tử <link>. Trang này cũng sử dụng bốn phông chữ web được yêu cầu dưới dạng tệp riêng biệt với tài nguyên CSS.

Biểu đồ dạng thác nước của mạng WebPageTest, trong đó có một tệp CSS bên ngoài có 4 phông chữ được tham chiếu. Trình quét tải trước sẽ phát hiện hình ảnh đề xuất LCP trong thời gian đến hạn.
Hình 12: Biểu đồ dạng thác nước của mạng WebPageTest về một trang web chạy trong Chrome trên thiết bị di động qua kết nối 3G mô phỏng. Đề xuất LCP của trang là một hình ảnh được tải từ phần tử <img>, nhưng trình quét tải trước phát hiện thấy nó vì CSS và phông chữ cần thiết cho việc tải trang trong các tài nguyên riêng biệt, giúp trình quét tải trước không thực hiện công việc của mình.

Điều gì sẽ xảy ra nếu CSS tất cả phông chữ được cùng dòng dưới dạng tài nguyên base64?

Biểu đồ dạng thác nước của mạng WebPageTest, trong đó có một tệp CSS bên ngoài có 4 phông chữ được tham chiếu. Trình quét tải trước bị chậm trễ đáng kể khi phát hiện hình ảnh LCP .
Hình 13: Biểu đồ dạng thác nước của mạng WebPageTest về một trang web chạy trong Chrome trên thiết bị di động qua kết nối 3G mô phỏng. Đề xuất LCP của trang là một hình ảnh được tải từ phần tử <img>, nhưng nội dung CSS và 4 tài nguyên phông chữ trong `` sẽ khiến trình quét tải trước phát hiện hình ảnh cho đến khi các tài nguyên đó được tải xuống hoàn toàn.

Tác động của cùng dòng gây ra hậu quả tiêu cực cho LCP trong ví dụ này và cho hiệu suất nói chung. Phiên bản của trang không có nội dung nào vẽ hình ảnh LCP trong khoảng 3,5 giây. Trang nội dòng mọi thứ chỉ vẽ hình ảnh LCP chỉ hơn 7 giây.

Không chỉ có trình quét tải trước, bạn còn có thể sử dụng nhiều tính năng khác tại đây. Phông chữ cùng dòng không phải là một chiến lược hay vì base64 là định dạng không hiệu quả đối với các tài nguyên nhị phân. Một yếu tố khác đang gặp phải là các tài nguyên phông chữ bên ngoài không được tải xuống trừ khi CSSOM xác định rằng các tài nguyên này là cần thiết. Khi các phông chữ đó được đặt cùng dòng dưới dạng base64, chúng sẽ được tải xuống cho dù chúng có cần thiết cho trang hiện tại hay không.

Tải trước có thể cải thiện mọi thứ ở đây không? Chắc chắn rồi. Bạn có thể tải trước hình ảnh LCP và giảm thời gian LCP, nhưng việc làm đầy HTML có thể không thể lưu vào bộ nhớ đệm với các tài nguyên cùng dòng sẽ gây ra những hậu quả tiêu cực khác về hiệu suất. Mẫu này cũng ảnh hưởng đến Thời gian hiển thị nội dung đầu tiên (FCP). Trong phiên bản của trang không có nội dung nào nằm trong dòng, FCP là khoảng 2,7 giây. Trong phiên bản có mọi nội dung cùng dòng, FCP là khoảng 5,8 giây.

Hãy cẩn thận với nội dung cùng dòng vào HTML, đặc biệt là các tài nguyên được mã hóa base64. Nhìn chung, bạn không nên dùng phương pháp này, trừ những tài nguyên rất nhỏ. Nội tuyến càng ít càng tốt vì nội tuyến quá nhiều sẽ gây cười cho lửa.

Hiển thị mã đánh dấu bằng JavaScript phía máy khách

Không nghi ngờ gì nữa về điều này: JavaScript chắc chắn ảnh hưởng đến tốc độ trang. Không chỉ các nhà phát triển dựa vào đó để mang lại khả năng tương tác, mà cũng có xu hướng dựa vào đó để phân phối nội dung. Điều này có thể mang lại trải nghiệm tốt hơn cho nhà phát triển. Tuy nhiên, lợi ích mà nhà phát triển mang lại không phải lúc nào cũng thành lợi ích cho người dùng.

Một mẫu có thể đánh bại trình quét tải trước là hiển thị mã đánh dấu bằng JavaScript phía máy khách:

Thác nước mạng WebPageTest hiển thị một trang cơ bản với hình ảnh và văn bản được hiển thị hoàn toàn trên ứng dụng khách trong JavaScript. Do thẻ đánh dấu nằm trong JavaScript, nên trình quét tải trước không thể phát hiện bất kỳ tài nguyên nào. Tất cả các tài nguyên đều bị trì hoãn do mạng và thời gian xử lý bổ sung mà khung JavaScript yêu cầu.
Hình 14: Biểu đồ dạng thác nước của mạng WebPageTest về một trang web do ứng dụng kết xuất chạy trên Chrome trên một thiết bị di động thông qua kết nối 3G mô phỏng. Vì nội dung có trong JavaScript và dựa vào khung để hiển thị, nên tài nguyên hình ảnh trong mã đánh dấu do ứng dụng kết xuất sẽ bị ẩn khỏi trình quét tải trước. Trải nghiệm tương đương do máy chủ hiển thị được mô tả trong Hình 9.

Khi tải trọng đánh dấu có trong và được hiển thị hoàn toàn bởi JavaScript trong trình duyệt, mọi tài nguyên trong mã đánh dấu đó sẽ không hiển thị với trình quét tải trước. Điều này làm chậm trễ quá trình khám phá các tài nguyên quan trọng, do đó chắc chắn sẽ ảnh hưởng đến LCP. Trong những ví dụ này, yêu cầu hình ảnh LCP bị chậm trễ đáng kể so với trải nghiệm tương đương do máy chủ hiển thị mà không yêu cầu JavaScript xuất hiện.

Điều này hơi khác với trọng tâm của bài viết này, nhưng ảnh hưởng của việc kết xuất đánh dấu trên ứng dụng khách vượt xa việc đánh bại trình quét tải trước. Thứ nhất, việc sử dụng JavaScript để hỗ trợ một trải nghiệm không yêu cầu sẽ tạo ra thời gian xử lý không cần thiết có thể ảnh hưởng đến Lượt tương tác với Nội dung hiển thị tiếp theo (INP).

Ngoài ra, việc hiển thị số lượng mã đánh dấu cực lớn trên máy khách có nhiều khả năng tạo ra tác vụ dài so với cùng một số lượng mã đánh dấu do máy chủ gửi. Lý do cho điều này – ngoài việc xử lý bổ sung mà JavaScript có liên quan – là các trình duyệt phát trực tuyến mã đánh dấu từ máy chủ và phân chia kết xuất theo cách tránh được các tác vụ dài. Mặt khác, mã đánh dấu do ứng dụng kết xuất được xử lý như một tác vụ đơn lẻ, có thể ảnh hưởng đến các chỉ số về khả năng phản hồi của trang như Tổng thời gian chặn (TBT) hoặc Thời gian phản hồi lần tương tác đầu tiên (FID) ngoài INP.

Cách khắc phục cho trường hợp này phụ thuộc vào câu trả lời cho câu hỏi này: Có lý do nào khiến máy chủ không thể cung cấp mã đánh dấu trang của bạn thay vì được hiển thị trên máy khách không? Nếu câu trả lời là "không", bạn nên xem xét chức năng hiển thị phía máy chủ (SSR) hoặc mã đánh dấu được tạo tĩnh nếu có thể, vì điều này sẽ giúp trình quét tải trước phát hiện và có thể tìm nạp trước các tài nguyên quan trọng.

Nếu trang của bạn cần đính kèm chức năng vào một số phần của mã đánh dấu trang, thì bạn vẫn có thể làm như vậy với SSR, với JavaScript vanilla hoặc hydration để tận dụng tối đa cả hai thành phần.

Giúp trình quét tải trước giúp bạn

Trình quét tải trước là một phương pháp tối ưu hoá trình duyệt hiệu quả cao, giúp các trang tải nhanh hơn trong quá trình khởi động. Bằng cách tránh các quy luật khiến hệ thống không thể phát hiện trước các tài nguyên quan trọng, bạn không chỉ giúp quá trình phát triển trở nên đơn giản hơn mà còn tạo ra trải nghiệm người dùng tốt hơn, mang lại kết quả tốt hơn về nhiều chỉ số, bao gồm cả một số chỉ số quan trọng về trang web.

Tóm lại, sau đây là những điều bạn cần lưu ý từ bài đăng này:

  • Trình duyệt tải trước trình quét là một trình phân tích cú pháp HTML phụ quét trước trình quét chính nếu trình duyệt bị chặn khám phá các tài nguyên có thể tìm nạp sớm hơn.
  • Trình quét tải trước không thể phát hiện các tài nguyên không có trong mã đánh dấu do máy chủ cung cấp trong yêu cầu điều hướng ban đầu. Có thể đánh bại trình quét tải trước theo những cách nào (nhưng không giới hạn ở):
    • Chèn tài nguyên vào DOM bằng JavaScript, có thể là tập lệnh, hình ảnh, biểu định kiểu hoặc bất cứ tài nguyên nào khác có thể tốt hơn trong tải trọng đánh dấu ban đầu từ máy chủ.
    • Tải từng phần hình ảnh hoặc iframe trong màn hình đầu tiên bằng giải pháp JavaScript.
    • Hiển thị mã đánh dấu trên ứng dụng có thể chứa tệp tham chiếu đến tài nguyên phụ của tài liệu bằng JavaScript.
  • Trình quét tải trước chỉ quét HTML. Tài liệu này không kiểm tra nội dung của các tài nguyên khác (cụ thể là Dịch vụ so sánh giá (CSS) mà có thể chứa thông tin tham chiếu đến các tài sản quan trọng, bao gồm cả các ứng viên LCP).

Nếu vì bất kỳ lý do gì mà bạn không thể tránh được mẫu ảnh hưởng tiêu cực đến khả năng tăng tốc hiệu suất tải của trình quét tải trước, hãy xem xét gợi ý về tài nguyên rel=preload. Nếu bạn sử dụng rel=preload, hãy thử nghiệm trong các công cụ trong phòng thí nghiệm để đảm bảo rằng ứng dụng mang lại hiệu quả mong muốn. Cuối cùng, đừng tải trước quá nhiều tài nguyên, vì khi bạn ưu tiên mọi tài nguyên, sẽ không có gì cả.

Tài nguyên

Hình ảnh chính từ Unsplash, của Mohammad Rahmani .