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

Tìm hiểu về trình quét tải trước của trình duyệt, cách trình quét này giúp cải thiện hiệu suất và cách bạn có thể tránh bị trình quét này làm phiền.

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

Một trong những cách tối ưu hoá nội bộ của trình duyệt là trình quét tải trước trình duyệ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à làm thế nào để bạn có thể tránh sử dụng trình quét 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ô rồi xử lý mã đó thành mô hình đối tượng. Quá trình 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ơ đồ cho thấy 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 phần tử <link> cho 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í là hiển thị bất kỳ phần tử nào trong đó) 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, tính năng hiển thị sẽ bị chặn để ngăn nhấp nháy nội dung chưa định kiểu (FOUC), đó là khi người dùng có thể nhìn thấy một phiên bản chưa định kiểu của một trang trong thời gian ngắn trước khi áp dụng kiểu.

Trang chủ web.dev ở trạng thái chưa định kiểu (trái) và trạng thái được định kiểu (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 đã áp dụng kiểu. Trạng thái chưa được định kiểu có thể xuất hiện 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 hoạt động 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.

Nguyên nhân là do trình duyệt không thể biết chắc chắn liệu có tập lệnh cụ thể nào sẽ sửa đổi DOM hay không trong khi trình phân tích cú pháp HTML chính vẫn đang thực hiện công việc của mình. Đây là lý do tại sao người ta thường tải JavaScript của bạn vào cuối tài liệu để những ảnh hưởng của việc phân tích cú pháp bị chặn và hiển thị là 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, bạn không nên chặn một trong hai bước quan trọng này vì chúng có thể làm chậm 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, trình duyệt 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ó tên 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). 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 nội dung 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 xét 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 mang tính suy đoán, nghĩa là chức năng này 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 thời điểm 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 tính năng kết xuất và phân tích cú pháp bị chặn. Nếu 2 vấn đề về 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 hữu ích lắm. Chìa khoá để xác định xem một 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 điều đó, bạn có thể đưa ra độ trễ giả tạo cho các yêu cầu để tìm hiểu vị trí mà trình quét tải trước đang hoạt động.

Hãy lấy trang này chứa văn bản và hình ảnh cơ bản cùng với biểu định kiểu làm ví dụ. Do tệp CSS chặn cả hiển thị và phân tích cú pháp, bạn đưa vào độ trễ giả tạo 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 thông tin 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: Một WebPageTest biểu đồ thác nước mạng về một trang web chạy trên Chrome trên thiết bị di động 3G mô phỏng. Mặc dù tệp kiểu được trì hoãn một cách nhân tạo thông qua proxy 2 giây trước khi bắt đầu tải, nhưng hình ảnh nằm sau trong tải trọng đánh dấu sẽ được trình quét tải trước phát hiện.

Như bạn có thể thấy trong thác nước, trình quét tải trước phát hiện phần tử <img> ngay cả khi tính năng kết xuất hình ảnh 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 hoá này, trình duyệt không thể tìm nạp nhanh mọi thứ trong khoảng thời gian chặn và nhiều yêu cầu tài nguyên sẽ liên tiếp thay vì đồng thời.

Sau khi xem ví dụ về đồ chơi đó, hãy cùng xem một số mẫu thực tế mà trình quét tải trước có thể bị đánh bại và những việc có thể làm để khắc phục các mẫu đó.

Chèn tập lệnh async

Giả sử bạn đã có HTML trong <head> bao gồm một số JavaScript cùng dòng như sau:

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

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

Các tập lệnh được chèn là async theo mặc định. Vì vậy, khi được chèn, tập lệnh này sẽ hoạt động như thể thuộc tính async đã được áp dụng cho tập lệnh đó. Điều đó có nghĩa là hoạt động này sẽ chạy sớm nhất có thể và không chặn quá trình kết xuất. 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 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 được thực hiện khi tập lệnh được chèn vào.
Hình 5: Biểu đồ dạng thác nước về mạng WebPageTest của một trang web chạy trên Chrome trên thiết bị di động qua kết nối 3G được mô phỏng. Trang này chứa một tệp định kiểu duy nhất và một tập lệnh async được chèn. Trình quét tải trước không thể phát hiện tập lệnh trong giai đoạn chặn hiển thị vì tập lệnh này được chèn trên máy khách.

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

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

Điều này chưa tối ưu vì yêu cầu cho tập lệnh chỉ xảy ra sau khi biểu định kiểu đã tải xuống xong. Điều này sẽ trì hoãn việc chạy tập lệnh sớm nhất có thể. Ngược lại, vì người dùng có thể tìm thấy 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ả:

Một thác nước mạng WebPageTest mô tả cách trình quét tải trước trình duyệt vẫn có thể phát hiện 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, 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ý một bảng định kiểu.
Hình 6: Biểu đồ thác nước của mạng WebPageTest của 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 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.

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

Một thác nước WebPageTest cho thấy cách sử dụng gợi ý tài nguyên rel=Tải trước để 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ể gây ra tác dụng phụ không mong muốn.
Hình 7: Biểu đồ thác nước của mạng WebPageTest của 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 duy nhất và một tập lệnh async được chèn, nhưng tập lệnh async được tải trước để đảm bảo tập lệnh này được phát hiện sớm hơn.

Tải trước "bản sửa lỗi" không thể giải quyết được vấn đề này, nhưng nó 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> – nhưng lại được tải ở mức "Low" (Thấp) mức độ ưu tiên, trong khi biểu định kiểu được tải ở mức "Cao nhất" mức độ ưu tiên. 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 ở "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ổ nhiều băng thông hơn 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à nguyên nhân làm 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 một 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. Thử nghiệm vị trí đặt phần tử <script> nếu cần, cũng như với các thuộc tính như deferasync.

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

Tải lười là một phương thức tuyệt vời để tiết kiệm dữ liệu, thường được á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 hình ảnh nằm "trong màn hình đầu tiên".

Điều này gây ra các vấn đề tiềm ẩn về khả năng phát hiện tài nguyên khi có liên quan đến trình quét tải trước và có thể trì hoãn thời gian cần thiết để phát hiện tham chiếu đến một hình ảnh, tải xuống, giải mã và hiển thị hình ảnh một cách không cần thiết. 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 phổ biến trong trình tải lười chạy bằng 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-, có nghĩa là trong ví dụ trước, data-src sẽ trở thành src. Bản 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 không có vấn đề gì cho đến khi được áp dụng cho những hình ảnh trong khung nhìn trong quá trình khởi động. Vì trình quét tải trước không đọc thuộc tính data-src theo cách tương tự như thuộc tính src (hoặc srcset), nên hệ thống không thể phát hiện tham chiếu hình ảnh sớm hơn. Tồi tệ hơn, hình ảnh sẽ bị trì hoãn tải cho đến sau khi JavaScript tải từng phần tải xuống, biên dịch và thực thi.

Biểu đồ thác nước mạng WebPageTest cho thấy hình ảnh được tải từng phần trong khung nhìn trong quá trình khởi động nhất thiết bị trễ do trình quét tải trước không thể tìm thấy tài nguyên hình ảnh và chỉ tải khi JavaScript cần để tính năng 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 dự kiến.
Hình 8: Biểu đồ thác nước của mạng WebPageTest của 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 khi khởi động. Điều 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), hình ảnh có thể là phần tử đề xuất cho Nội dung lớn nhất hiển thị (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 (có thể là tại thời điểm mà (các) khối biểu định kiểu của trang hiển thị) LCP.

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 trong quá trình khởi động, vì trình quét tải trước sẽ phát hiện và tìm nạp tài nguyên hình ảnh nhanh hơn.

Biểu đồ dạng thác nước về mạng của WebPageTest mô tả một tình huống tải hình ảnh trong khung nhìn trong quá trình khởi động. Hình ảnh không được tải từng phần, có nghĩa là hình ảnh không phụ thuộc vào tập lệnh tải, có 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 đồ thác nước của mạng WebPageTest của một trang web chạy trong Chrome trên thiết bị di độ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 quá trình tải hình ảnh.

Kết quả trong ví dụ đơn giản này là sự cải thiện LCP 100 mili giây trên kết nối chậm. Điều này có vẻ không phải là một điểm cải tiến lớn, nhưng đó là khi bạn xem xét giải pháp này là một cách khắc phục nhanh về mã đánh dấu và hầu hết các trang web đều phức tạp hơn bộ ví dụ này. Điều đó có nghĩa là các đề xuất LCP có thể phải cạnh tranh băng thông với nhiều tài nguyên khác, vì vậy, các hoạt động 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 sẽ 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 việc tìm nạp những hình ảnh được tham chiếu bởi thuộc tính background-image.

Giống như HTML, các trình duyệt xử lý CSS thành mô hình đối tượng riêng của nó, đượ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 tạo, thì các tài nguyên đó sẽ được yêu cầu tại thời điểm phát hiện chứ không phải bởi 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 background-image CSS. Dưới đâ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 ứng viên LCP nằm trong một loại tài nguyên mà trình quét tải trước của trình duyệt không thể kiểm tra, nên tài nguyên sẽ bị trì hoãn tải cho đến khi CSS được tải xuống và xử lý, làm trì hoãn thời gian hiển thị của ứng viên LCP.
Hình 10: Biểu đồ thác nước của mạng WebPageTest của một trang web chạy trong Chrome trên một thiết bị di độ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 của CSS (hàng 3). Hình ảnh mà công cụ này yêu cầu sẽ 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 hình ảnh đó.

Trong trường hợp này, trình quét tải trước không bị ảnh hưởng quá nhiều vì không có liên quan. Mặc dù vậy, nếu một đề xuất LCP trên trang đến từ một thuộc tính CSS background-image, thì 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 ra hình ảnh sớm hơn:

Biểu đồ thác nước của mạng WebPageTest cho thấy hình nền CSS (là đề xuất LCP) đang tải sớm hơn nhiều do sử dụng gợi ý rel=tải trước. Thời gian LCP tăng khoảng 250 mili giây.
Hình 11: Biểu đồ thác nước của mạng WebPageTest của một trang web chạy trong Chrome trên một thiết bị di độ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 của 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, đề xuất LCP được phát hiện sớm hơn, giúp giảm thời gian LCP. Mặc dù gợi ý đó giúp khắc phục vấn đề này, nhưng tốt hơn hết bạn nên đánh giá xem liệu đề xuất LCP hình ảnh của mình phải tải từ CSS hay không. Với thẻ <img>, bạn sẽ có nhiều quyền kiểm soát hơn đối với 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 đó.

Cùng dòng quá nhiều tài nguyên

Cùng dòng là một phương pháp đặt tài nguyên bên trong HTML. Bạn có thể cùng dòng biểu định kiểu trong phần tử <style>, tập lệnh trong phần tử <script> và hầu như 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 so với việc tải chúng xuống vì không có yêu cầu riêng cho tài nguyên. Tệp này nằm ngay trong tài liệu và tải ngay lập tức. Tuy nhiên, có một số hạn chế đáng kể:

  • Nếu bạn không lưu vào bộ nhớ đệm HTML và thực sự không thể lưu nếu phản hồi HTML là động – 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 nội tuyến không thể sử dụng lại.
  • Ngay cả khi bạn có thể lưu HTML vào bộ nhớ đệm, tài nguyên cùng dòng 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 nội tuyến quá nhiều, bạn sẽ trì hoãn việc trình quét tải trước phát hiện các tài nguyên sau này trong tài liệu, 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 do phần tử <link> tải. Trang cũng sử dụng 4 phông chữ web được yêu cầu dưới dạng các tệp riêng biệt với tài nguyên CSS.

Biểu đồ thác nước của mạng WebPageTest có tệp CSS bên ngoài với 4 phông chữ được tham chiếu trong đó. Trình quét tải trước sẽ phát hiện hình ảnh ứng viên LCP.
Hình 12: Biểu đồ thác nước của mạng WebPageTest của một trang web chạy trong Chrome trên một 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 được trình quét tải trước phát hiện vì CSS và phông chữ cần thiết để tải trang trong các tài nguyên riêng biệt. Điều này không trì hoãn việc trình quét tải trước thực hiện công việc.

Bây giờ, đ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 đồ thác nước của mạng WebPageTest có tệp CSS bên ngoài với 4 phông chữ được tham chiếu trong đó. Trình quét tải trước bị trì hoãn đáng kể khi phát hiện hình ảnh LCP.
Hình 13: Biểu đồ thác nước của mạng WebPageTest của một trang web chạy trong Chrome trên một 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 việc nội tuyến CSS và 4 tài nguyên phông chữ của CSS trong " sẽ trì hoãn việc trình quét tải trước phát hiện hình ảnh cho đến khi tải xong các tài nguyên đó.

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

Ngoài 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 ở đây. Phông chữ cùng dòng không phải là một chiến lược tốt vì base64 là định dạng không hiệu quả cho các tài nguyên nhị phân. Một yếu tố khác đang diễn ra là các tài nguyên phông chữ bên ngoài sẽ không được tải xuống trừ khi CSSOM xác định rằng các tài nguyên đó là cần thiết. Khi các phông chữ đó được nội tuyến dưới dạng base64, chúng sẽ được tải xuống cho dù trang hiện tại có cần hay không.

Tính năng tải trước có thể giúp cải thiện thông tin ở đâ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. Tuy nhiên, việc làm tăng tối đa 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 cũng gây ra những hậu quả tiêu cực khác. Mẫu này cũng ảnh hưởng đến Nội dung đầu tiên hiển thị (FCP). Trong phiên bản của trang không có nội dung cùng dòng, FCP là khoảng 2,7 giây. Trong phiên bản mà mọi thứ cùng dòng, FCP là khoảng 5,8 giây.

Hãy thật cẩn thận với việc chèn nội dung vào HTML, đặc biệt là các tài nguyên được mã hoá base64. Nhìn chung, không nên dùng tuỳ chọn này, ngoại trừ các tài nguyên rất nhỏ. Hãy sử dụng nội tuyến càng ít càng tốt, vì việc sử dụng nội tuyến quá nhiều là một hành động nguy hiểm.

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

Không có gì phải nghi ngờ: JavaScript chắc chắn ảnh hưởng đến tốc độ trang. Các nhà phát triển không chỉ dựa vào công cụ này để cung cấp tính tương tác mà còn có xu hướng dựa vào công cụ này để cung cấp nội dung. Điều này dẫn đến trải nghiệm tốt hơn cho nhà phát triển theo một số cách; nhưng lợi ích cho nhà phát triển không phải lúc nào cũng chuyển thành lợi ích cho người dùng.

Một mẫu có thể vượt qua trình quét tải trước là kết xuất mã đánh dấu bằng JavaScript phía máy khách:

Thác nước trên mạng WebPageTest cho thấy 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 trong JavaScript. Vì mã đá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 cũng 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 đồ thác nước của mạng WebPageTest của một trang web do ứng dụng hiển thị chạy trên Chrome trên thiết bị di động qua kết nối 3G mô phỏng. Vì nội dung nằm trong JavaScript và dựa vào một 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 được kết xuất phía máy chủ được mô tả trong Hình 9.

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

Mặc dù nội dung này có phần khác biệt so với trọng tâm của bài viết này, nhưng tác động của việc kết xuất mã đánh dấu trên ứng dụng không chỉ dừng lại ở chức năng quét tải trước. Thứ nhất, việc giới thiệu JavaScript để hỗ trợ trải nghiệm không yêu cầu sẽ gây 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). Nếu hiển thị quá nhiều mã đánh dấu trên máy khách, bạn sẽ có nhiều khả năng tạo ra các nhiệm vụ dài hơn so với cùng một số lượng mã đánh dấu mà máy chủ gửi. Lý do cho điều này – ngoài việc xử lý bổ sung liên quan đến JavaScript – là do các trình duyệt truyền trực tuyến mã đánh dấu từ máy chủ và phân chia hiển thị theo cách có xu hướng hạn chế các tác vụ dài. Mặt khác, thẻ đánh dấu do ứng dụng kết xuất được xử lý dưới dạng một tác vụ nguyên khối, có thể ảnh hưởng đến INP của một trang.

Giải pháp cho trường hợp này sẽ phụ thuộc vào câu trả lời cho câu hỏi sau: Có lý do nào khiến máy chủ không thể cung cấp mã đánh dấu trên trang của bạn chứ không thể hiển thị trên máy khách? Nếu câu trả lời cho câu hỏi này là "không", thì bạn nên cân nhắc phương thức kết xuất phía máy chủ (SSR) hoặc mã đánh dấu được tạo tĩnh, 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 phải có JavaScript để đí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 vani) hoặc dùng công nghệ hydrat hoá để tận dụng tối đa cả hai phương pháp trê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 tính năng tối ưu hoá trình duyệt hiệu quả cao giúp trang tải nhanh hơn trong khi khởi động. Bằng cách tránh các mẫu không thể khám phá trước tài nguyên quan trọng, bạn không chỉ đơn giản hoá quá trình phát triể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 vài chỉ số quan trọng của trang web.

Tóm lại, sau đây là những điểm mà bạn nên rút ra từ bài đăng này:

  • Trình quét tải trước của trình duyệ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 quét này bị chặn để có thể khám phá các tài nguyên mà trình duyệt có thể tìm nạp sớm hơn.
  • Trình quét tải trước không phát hiện được những 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. (nhưng không giới hạn ở) các cách vượt qua trình quét tải trước:
    • Chèn tài nguyên vào DOM bằng JavaScript, cho dù đó là tập lệnh, hình ảnh, tệp định kiểu hay bất kỳ nội dung nào khác sẽ tốt hơn trong tải trọng đánh dấu ban đầu từ máy chủ.
    • Tải lười hình ảnh hoặc iframe ở đầu trang bằng giải pháp JavaScript.
    • Hiển thị mã đánh dấu trên ứng dụng có thể chứa nội dung tham chiếu đến các nguồn tài liệu phụ bằng JavaScript.
  • Trình quét tải trước chỉ quét HTML. Công cụ này không kiểm tra nội dung của các tài nguyên khác (đặc biệt là CSS) mà có thể bao gồm các tệp đối chiếu đến các tài sản quan trọng, bao gồm cả các đề xuất LCP.

Nếu vì lý do nào đó, bạn không thể tránh được một mẫu ảnh hưởng tiêu cực đến khả năng của trình quét tải trước để tăng tốc hiệu suất tải, hãy cân nhắc gợi ý tài nguyên rel=preload. Nếu bạn sử dụng rel=preload, hãy kiểm thử trong các công cụ của phòng thí nghiệm để đảm bảo rằng hiệu ứng này mang lại cho bạn 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 thứ, sẽ không có gì.

Tài nguyên

Hình ảnh chính trên Unsplash, của Mohammad Rahmani .