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 trong việc tối ưu hoá tốc độ trang là cần biết một chút về nội bộ trình duyệt. Trình duyệt thực hiện một số hoạt động tối ưu hoá để cải thiện hiệu suất theo những cách mà chúng ta (với tư cách là nhà phát triển) không thể làm được, nhưng chỉ khi những hoạt động tối ưu hoá đó không bị cản trở một cách vô tình.

Một tính năng tối ưu hoá trình duyệt nội bộ mà bạn cần hiểu là trình quét tải trước trình duyệt. Bài đăng này sẽ trình bày 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 cản trở 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 tạo mã thông báo cho mã đánh dấu thô và xử lý mã đó thành mô hình đối tượng. Tất cả đều diễn ra suôn sẻ cho đến khi trình phân tích cú pháp tạm dừng khi tìm thấy tài nguyên chặn, chẳng hạn như một tệp 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> 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ẽ gặp phải phần tử <link> cho tệp CSS bên ngoài, khiến trình duyệt không phân tích cú pháp được phần còn lại của tài liệu hoặc thậm chí không hiển thị được phần nào của tài liệu đó 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, quá trình kết xuất bị chặn để ngăn chặn nội dung không có kiểu (FOUC), tức là khi bạn có thể thấy một phiên bản không có kiểu của trang trong giây lát trước khi các kiểu được áp dụng cho trang đó.

Trang chủ web.dev ở trạng thái chưa được định kiểu (bên trái) và trạng thái đã được định 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 đã áp dụng kiểu. Trạng thái chưa được định kiểu có thể xảy ra trong nháy mắt nếu trình duyệt không chặn quá trình kết xuất trong khi tệp kiểu đang được tải xuống và xử lý.

Trình duyệt cũng chặn việc 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à trình duyệt không thể biết chắc chắn liệu bất kỳ tập lệnh nào có 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. Đó là lý do tại sao bạn thường tải JavaScript ở cuối tài liệu để các hiệu ứ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 khiến trình duyệt nên chặn cả việ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ì việc này 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), đây 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 các thành phần một cách dự đoán. Tại đâ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>, nhưng 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 tài nguyên đó 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à dự đoán, nghĩa là trình quét này kiểm tra mã đánh dấu thô để tìm tài nguyên cần tìm nạp khi có cơ hội trước khi trình phân tích cú pháp HTML chính phát hiện các tài nguyên đó.

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 quá trình kết xuất và phân tích cú pháp bị chặn. Nếu hai vấn đề về hiệu suất này không bao giờ xảy ra, thì trình quét tải trước sẽ không hữu ích lắm. Để biết một trang web có được hưởng lợi từ trình quét tải trước hay không, bạn cần xem xét những hiện tượng chặn này. Để làm việc đó, bạn có thể tạo độ trễ nhân tạo cho các yêu cầu để tìm hiểu vị trí trình quét tải trước đang hoạt động.

Hãy lấy trang này gồm văn bản và hình ảnh cơ bản có một tệp kiểu làm ví dụ. Vì các tệp CSS chặn cả quá trình hiển thị và phân tích cú pháp, nên bạn sẽ tạo độ trễ nhân tạo là 2 giây cho tệp 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 đồ dạng thác nước về mạng của WebPageTest minh hoạ độ trễ nhân tạo là 2 giây áp dụng cho trang tính kiểu.
Hình 4: Biểu đồ dạng thác nước về mạng của WebPageTest cho 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. 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 sẽ phát hiện phần tử <img> ngay cả khi quá trình hiển thị 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 các nội dung một cách cơ hội 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ế có thể đánh bại trình quét tải trước 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>

Theo mặc định, các tập lệnh được chèn là async, 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à quy trình 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 giả định rằng <script> cùng dòng này nằm sau phần tử <link> tải tệp CSS bên ngoài, thì bạn sẽ nhận được kết quả không tối ưu:

Biểu đồ WebPageTest này cho thấy quá trình quét tải trước bị đánh bại khi một tập lệnh được chèn.
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 kiểu 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. Tại 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. Tại 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 tải tệp kiểu và JavaScript nội tuyến chèn tập lệnh async sau tệp kiểu đó 2,6 giây, nên chức năng mà tập lệnh cung cấp không có sẵn sớm nhất có thể.

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

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 đồ 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 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ẽ hiệu quả, nhưng có thể gây ra một số tác dụng phụ. Xét cho cùng, tại sao bạn phải sử dụng rel=preload để khắc phục vấn đề mà bạ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=preload để thúc đẩy việc khám phá tập lệnh được chèn không đồng bộ, mặc dù có thể có các hiệu ứng phụ ngoài mong muốn.
Hình 7: 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 kiểu 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 được phát hiện sớm hơn.

Tính năng tải trước "khắc phục" vấn đề ở đây, nhưng lại 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 đó, tệp định kiểu được tải ở mức độ ưu tiên "Cao nhất". Trong bản minh hoạ cuối cùng, tập lệnh async được tải trước, nhưng vẫn tải bảng định kiểu ở 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 "Cao".

Khi mức độ ưu tiên của một tài nguyên được 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ù tệp kiểu có mức độ ưu tiên cao nhất – nhưng mức độ ưu tiên cao 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ố gây ra tình trạng 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 khi cần với vị trí đặt phần tử <script>, 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 lười được áp dụng không chính xác cho các hình ảnh "ở đầu trang".

Điều này gây ra các vấn đề tiềm ẩn về khả năng khám phá tài nguyên liên quan đến trình quét tải trước và có thể làm chậm thời gian khám phá tham chiếu đến hình ảnh, tải hình ảnh 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 sau:

<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 lười sẽ xoá tiền tố data-, 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 gây ra vấn đề cho đến khi được áp dụng cho các hình ảnh nằm 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 tham chiếu hình ảnh không được phát hiện sớm hơn. Tệ hơn, hình ảnh bị trì hoãn tải cho đến sau khi trình tải lười JavaScript tải xuống, biên dịch và thực thi.

Biểu đồ dạng thác nước về mạng WebPageTest cho thấy một hình ảnh tải lười trong khung nhìn trong quá trình khởi động bị trì hoãn như thế nào vì trình quét tải trước của 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ính năng tải lười 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 đồ 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. Tài nguyên hình ảnh được tải từng phần 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. Điều này làm mất hiệu lực của 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 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 một cách suy đoán (có thể là trong thời điểm (các) bảng định kiểu của trang chặn quá trình kết xuất), thì LCP sẽ 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 nằm trong khung nhìn trong quá trình 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 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, tức là 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 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. 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 tài nguyên hình ảnh sớm hơn.

Kết quả trong ví dụ đơn giản này là cải thiện 100 mili giây về LCP 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 quét tải trước của trình duyệt sẽ quét đánh dấu. Trình quét 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 hình ảnh được tham chiếu bằng thuộc tính background-image.

Giống như HTML, 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 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. Sau đây là những gì xảy ra khi tài nguyên tải:

Biểu đồ dạng thác nước về mạng WebPageTest mô tả một trang có đề xuất LCP được tải từ CSS bằng thuộc tính background-image. 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 của trình duyệt không thể kiểm tra, nên tài nguyên này bị trì hoãn tải cho đến khi CSS được tải xuống và xử lý, làm chậm thời gian vẽ của đề xuất LCP.
Hình 10: 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. Đề xuất LCP của trang là một phần tử có thuộc tính CSS background-image (hàng 3). Hình ảnh mà nó 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 bại mà là không liên quan. Tuy nhiên, nếu một đề xuất LCP trên trang là từ một thuộc tính CSS background-image, bạn sẽ muố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:

Biểu đồ dạng thác nước về mạng WebPageTest cho thấy hình nền CSS (là đề xuất LCP) tải nhanh hơn nhiều do sử dụng gợi ý rel=preload. Thời gian LCP cải thiện khoảng 250 mili giây.
Hình 11: 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. Đề xuất LCP của trang là một phần tử có thuộc tính CSS background-image (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 đó.

Nhúng quá nhiều tài nguyên

Nội tuyến là một phương pháp đặt tài nguyên bên trong HTML. Bạn có thể nội tuyến các tệp 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 mã hoá base64.

Việc nội tuyến tài nguyên có thể nhanh hơn so với việc tải tài nguyên xuống vì không có yêu cầu riêng biệt nào được đưa ra cho tài nguyên. Hình ảnh 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 HTML vào bộ nhớ đệm (và bạn không thể lưu nếu phản hồi HTML là động), thì các tài nguyên nội tuyến sẽ 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, các tài nguyên nội tuyến sẽ 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 sử dụng 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 nội dung bổ sung, nội tuyến đó xuống sẽ mất nhiều thời gian hơn.

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 do phần tử <link> tải. Trang này cũng sử dụng 4 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 về mạng của WebPageTest cho trang có tệp CSS bên ngoài với 4 phông chữ được tham chiếu trong đó. Hình ảnh đề xuất LCP sẽ được trình quét tải trước phát hiện đúng lúc.
Hình 12: 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. Đề 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, không làm chậm trình quét tải trước thực hiện công việc của mình.

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 đồ dạng thác nước về mạng của WebPageTest cho trang 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ể trong việc phát hiện hình ảnh LCP .
Hình 13: 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. Đề 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 đó.

Ảnh hưởng của việc nội tuyến sẽ dẫn đến những hậu quả tiêu cực đối với LCP trong ví dụ này và đối với hiệu suất nói chung. Phiên bản trang không nội tuyến bất kỳ nội dung nào sẽ vẽ hình ảnh LCP trong khoảng 3,5 giây. Trang nội tuyến mọi thứ không vẽ hình ảnh LCP cho đến hơn 7 giây.

Có nhiều yếu tố khác ngoài trình quét tải trước. Việc nội tuyến phông chữ không phải là một chiến lược hay 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 cũng đóng vai trò quan trọng 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 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ể cải thiện vấn đề nà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 phồng HTML có thể không lưu vào bộ nhớ đệm bằng các tài nguyên nội tuyến sẽ gây ra những hậu quả tiêu cực khác về hiệu suất. Hiển thị nội dung đầu tiên (FCP) cũng chịu ảnh hưởng của mẫu này. Trong phiên bản trang không có nội dung nào được nội tuyến, FCP là khoảng 2,7 giây. Trong phiên bản mà mọi thứ đều được nội tuyến, FCP là khoảng 5,8 giây.

Hãy cẩn thận khi đưa nội dung vào HTML, đặc biệt là các tài nguyên được mã hoá base64. Nói chung, bạn không nên làm như vậ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 liều lĩnh.

Kết xuất 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. Nhà phát triển không chỉ dựa vào API này để cung cấp tính tương tác mà còn có xu hướng dựa vào API này để tự phân phối nội dung. Điều này giúp nhà phát triển có trải nghiệm tốt hơ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 mang lại 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à kết xuất mã đánh dấu bằng JavaScript phía máy khách:

Một thác nước mạng WebPageTest hiển thị một trang cơ bản có hình ảnh và văn bản được kết xuất hoàn toàn trên máy khách bằng 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ả tài nguyên đều bị trì hoãn thêm do thời gian xử lý và mạng bổ sung mà khung JavaScript yêu cầu.
Hình 14: Biểu đồ dạng thác nước về 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 được 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 hiển thị 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 trọng tải đánh dấu được chứa trong và hiển thị hoàn toàn bằng JavaScript trong trình duyệt, mọi tài nguyên trong đánh dấu đó sẽ không hiển thị hiệu quả với trình quét tải trước. Đ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 các ví dụ này, yêu cầu về hình ảnh LCP bị trì hoãn một cách đá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 hiệu ứng của việc kết xuất mã đánh dấu trên ứng dụng khách còn vượt xa việc đánh bại trình quét tải trước. Một lý do là việc giới thiệu JavaScript để hỗ trợ trải nghiệm không yêu cầu JavaScript sẽ làm tăng thời gian xử lý không cần thiết, có thể ảnh hưởng đến Lượt tương tác đến lượt vẽ tiếp theo (INP). Việc hiển thị một lượng lớn mã đánh dấu trên máy khách có nhiều khả năng tạo ra các tác vụ dài so với cùng một lượng mã đánh dấu do máy chủ gửi. Lý do cho việc này (ngoài việc JavaScript cần xử lý thêm) là trình duyệt truyền trực tuyến mã đánh dấu từ máy chủ và phân đoạn quá trình kết xuất theo cách có xu hướng giới hạn các tác vụ dài. Mặt khác, mã đánh dấu do máy khách hiển thị được xử lý dưới dạng một tác vụ đơn lẻ, nguyên khối, có thể ảnh hưởng đến INP của trang.

Giải pháp 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 của trang thay vì 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 cân nhắc việc kết xuất phía máy chủ (SSR) hoặc tạo mã đánh dấu tĩnh nếu có thể, vì điều này sẽ giúp trình quét tải trước khám phá và tìm nạp trước các tài nguyên quan trọng một cách kịp thời.

Nếu trang của bạn cần 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ể thực hiện việc này bằng SSR, bằng JavaScript cơ bản hoặc hydratation (tái tạo) để tận dụng tối đa cả hai phương pháp.

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 rất hiệu quả, 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 mẫu làm giảm khả năng phát hiện trước các tài nguyên quan trọng, bạn không chỉ đơn giản hoá quá trình phát triển cho chính mình mà còn tạo ra trải nghiệm người dùng tốt hơn, giúp mang lại kết quả tốt hơn theo 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 ghi nhớ 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 phân tích cú pháp chính nếu trình phân tích cú pháp chính bị chặn để phát hiện 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 trên yêu cầu điều hướng ban đầu. Có thể đánh bại trình quét tải trước bằng các cách sau (nhưng không giới hạn ở):
    • 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 hoặc 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 các 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. Trình phân tích 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) có thể chứa các tệp tham chiếu đến các thành phầ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 công cụ này mang lại cho bạn hiệu ứng 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ứ, thì sẽ không có tài nguyên nào được ưu tiên.

Tài nguyên

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