Tải từng phần hình ảnh và các phần tử <iframe>

Hình ảnh và các phần tử <iframe> thường tiêu thụ nhiều băng thông hơn các loại tài nguyên khác. Trong trường hợp các phần tử <iframe>, có thể mất thêm một khoảng thời gian xử lý hợp lý để tải và hiển thị các trang trong đó.

Trong trường hợp tải hình ảnh theo cách trì hoãn, việc trì hoãn tải những hình ảnh nằm ngoài khung hiển thị ban đầu có thể giúp giảm tình trạng tranh chấp băng thông cho các tài nguyên quan trọng hơn trong khung hiển thị ban đầu. Trong một số trường hợp kết nối mạng kém, điều này có thể cải thiện Thời gian hiển thị nội dung lớn nhất (LCP) của trang và băng thông được phân bổ lại có thể giúp các thành phần LCP tải và hiển thị nhanh hơn.

Đối với các phần tử <iframe>, bạn có thể cải thiện Lượt tương tác đến nội dung hiển thị tiếp theo (INP) của một trang trong quá trình khởi động bằng cách tải chúng một cách trì hoãn. Lý do là vì <iframe> là một tài liệu HTML hoàn toàn riêng biệt với các tài nguyên phụ riêng. Mặc dù các phần tử <iframe> có thể chạy trong một quy trình riêng biệt, nhưng không hiếm khi chúng chia sẻ một quy trình với các luồng khác, điều này có thể tạo ra các điều kiện khiến các trang ít phản hồi hơn đối với thông tin đầu vào của người dùng.

Do đó, việc hoãn tải hình ảnh và các phần tử <iframe> ngoài màn hình là một kỹ thuật đáng theo đuổi và chỉ cần bỏ ra khá ít công sức để có được lợi nhuận tương đối tốt về hiệu suất. Mô-đun này giải thích cách tải trì hoãn hai loại phần tử này để mang lại trải nghiệm người dùng nhanh hơn và tốt hơn trong khoảng thời gian khởi động quan trọng của trang.

Tải từng phần hình ảnh bằng thuộc tính loading

Bạn có thể thêm thuộc tính loading vào các phần tử <img> để cho trình duyệt biết cách tải các phần tử đó:

  • "eager" thông báo cho trình duyệt rằng hình ảnh phải được tải ngay lập tức, ngay cả khi hình ảnh đó nằm ngoài khung hiển thị ban đầu. Đây cũng là giá trị mặc định cho thuộc tính loading.
  • "lazy" hoãn tải hình ảnh cho đến khi hình ảnh nằm trong một khoảng cách nhất định so với khung nhìn hiển thị. Khoảng cách này tuỳ thuộc vào trình duyệt, nhưng thường được đặt đủ lớn để hình ảnh tải vào thời điểm người dùng cuộn đến hình ảnh đó.

Bạn cũng nên lưu ý rằng nếu đang sử dụng phần tử <picture>, bạn vẫn phải áp dụng thuộc tính loading cho phần tử con <img>, chứ không phải chính phần tử <picture>. Điều này là do phần tử <picture> là một vùng chứa chứa các phần tử <source> bổ sung trỏ đến các đề xuất hình ảnh khác nhau và đề xuất mà trình duyệt chọn sẽ được áp dụng trực tiếp cho phần tử <img> con của phần tử đó.

Không tải từng phần những hình ảnh nằm trong khung hiển thị ban đầu

Bạn chỉ nên thêm thuộc tính loading="lazy" vào các phần tử <img> được đặt bên ngoài khung hiển thị ban đầu. Tuy nhiên, có thể sẽ phức tạp khi biết vị trí chính xác của một phần tử tương đối trong khung nhìn trước khi trang được kết xuất. Bạn phải cân nhắc các kích thước khung nhìn, tỷ lệ khung hình và thiết bị khác nhau.

Ví dụ: khung hiển thị trên máy tính có thể khác với khung hiển thị trên điện thoại di động vì khung hiển thị này hiển thị nhiều không gian dọc hơn, có thể vừa với những hình ảnh trong khung hiển thị ban đầu mà không xuất hiện trong khung hiển thị ban đầu của một thiết bị có kích thước nhỏ hơn. Máy tính bảng được dùng ở hướng dọc cũng hiển thị một lượng đáng kể không gian dọc, thậm chí có thể nhiều hơn một số thiết bị máy tính.

Tuy nhiên, có một số trường hợp khá rõ ràng mà bạn nên tránh áp dụng loading="lazy". Ví dụ: bạn chắc chắn nên bỏ qua thuộc tính loading="lazy" trong các phần tử <img> trong trường hợp hình ảnh chính hoặc các trường hợp sử dụng hình ảnh khác mà các phần tử <img> có khả năng xuất hiện ở phía trên dòng đầu tiên hoặc gần đầu bố cục trên mọi thiết bị. Điều này càng quan trọng hơn đối với những hình ảnh có khả năng là LCP.

Những hình ảnh được tải từng phần cần đợi trình duyệt hoàn tất bố cục để biết liệu vị trí cuối cùng của hình ảnh có nằm trong khung hiển thị hay không. Điều này có nghĩa là nếu một phần tử <img> trong khung hiển thị có thể nhìn thấy có thuộc tính loading="lazy", thì phần tử đó chỉ được yêu cầu sau khi tất cả CSS được tải xuống, phân tích cú pháp và áp dụng cho trang – thay vì được tìm nạp ngay khi trình quét tải trước phát hiện thấy phần tử đó trong mã đánh dấu thô.

Vì thuộc tính loading trên phần tử <img> được hỗ trợ trên tất cả các trình duyệt chính nên bạn không cần sử dụng JavaScript để tải hình ảnh một cách trì hoãn. Việc thêm JavaScript bổ sung vào một trang để cung cấp các chức năng mà trình duyệt đã cung cấp sẽ ảnh hưởng đến các khía cạnh khác của hiệu suất trang, chẳng hạn như INP.

Bản minh hoạ tính năng tải từng phần hình ảnh

Tải từng phần các phần tử <iframe>

Việc tải từng phần các phần tử <iframe> cho đến khi chúng xuất hiện trong khung nhìn có thể giúp bạn tiết kiệm đáng kể dữ liệu và cải thiện tốc độ tải các tài nguyên quan trọng cần thiết để tải trang cấp cao nhất. Ngoài ra, vì các phần tử <iframe> về cơ bản là toàn bộ tài liệu HTML được tải trong một tài liệu cấp cao nhất, nên chúng có thể bao gồm một số lượng đáng kể các tài nguyên phụ (đặc biệt là JavaScript), điều này có thể ảnh hưởng đáng kể đến INP của một trang nếu các tác vụ trong những khung hình đó đòi hỏi thời gian xử lý đáng kể.

Nội dung nhúng của bên thứ ba là một trường hợp sử dụng phổ biến cho các phần tử <iframe>. Ví dụ: trình phát video được nhúng hoặc bài đăng trên mạng xã hội thường sử dụng các phần tử <iframe> và chúng thường yêu cầu một số lượng đáng kể các tài nguyên phụ. Điều này cũng có thể dẫn đến tình trạng tranh chấp băng thông cho các tài nguyên của trang cấp cao nhất. Ví dụ: việc tải từng phần một video được nhúng trên YouTube giúp tiết kiệm hơn 500 KiB trong lần tải trang ban đầu, trong khi việc tải từng phần một trình bổ trợ nút Thích của Facebook giúp tiết kiệm hơn 200 KiB (phần lớn là JavaScript).

Dù bằng cách nào, bất cứ khi nào có một <iframe> dưới màn hình đầu tiên trên một trang, bạn nên cân nhắc kỹ việc tải từng phần nếu không cần thiết phải tải trước, vì việc này có thể cải thiện đáng kể trải nghiệm người dùng.

Thuộc tính loading cho các phần tử <iframe>

Thuộc tính loading trên các phần tử <iframe> cũng được hỗ trợ trong tất cả các trình duyệt chính. Các giá trị cho thuộc tính loading và hành vi của các giá trị này giống như các phần tử <img> sử dụng thuộc tính loading:

  • "eager" là giá trị mặc định. Thẻ này thông báo cho trình duyệt tải HTML của phần tử <iframe> và các tài nguyên phụ của phần tử đó ngay lập tức.
  • "lazy" trì hoãn việc tải HTML của phần tử <iframe> và các tài nguyên phụ của phần tử đó cho đến khi phần tử đó nằm trong một khoảng cách xác định trước so với khung nhìn.

Bản minh hoạ về tính năng tải từng phần iframe

Mặt tiền

Thay vì tải một thành phần được nhúng ngay trong quá trình tải trang, bạn có thể tải thành phần đó theo yêu cầu để phản hồi một lượt tương tác của người dùng. Bạn có thể thực hiện việc này bằng cách hiện một hình ảnh hoặc một phần tử HTML thích hợp khác cho đến khi người dùng tương tác với phần tử đó. Sau khi người dùng tương tác với phần tử này, bạn có thể thay thế phần tử đó bằng thành phần nhúng của bên thứ ba. Kỹ thuật này được gọi là facade (mặt nạ).

Một trường hợp sử dụng phổ biến cho các lớp mặt nạ là video được nhúng từ các dịch vụ bên thứ ba, trong đó video được nhúng có thể liên quan đến việc tải nhiều tài nguyên phụ bổ sung và có khả năng tốn kém (chẳng hạn như JavaScript) ngoài nội dung video. Trong trường hợp như vậy (trừ phi có nhu cầu chính đáng để video tự động phát), video được nhúng yêu cầu người dùng tương tác với video trước khi phát bằng cách nhấp vào nút phát.

Đây là cơ hội tuyệt vời để hiển thị một hình ảnh tĩnh có hình ảnh tương tự như video được nhúng và tiết kiệm đáng kể băng thông trong quá trình này. Sau khi người dùng nhấp vào hình ảnh, hình ảnh đó sẽ được thay thế bằng thành phần <iframe> được nhúng thực tế, thành phần này sẽ kích hoạt HTML của phần tử <iframe> bên thứ ba và các tài nguyên phụ của phần tử đó để bắt đầu tải xuống.

Ngoài việc cải thiện thời gian tải trang ban đầu, một lợi ích quan trọng khác là nếu người dùng không bao giờ phát video, thì các tài nguyên cần thiết để phân phối video đó sẽ không bao giờ được tải xuống. Đây là một mẫu hình hay vì nó đảm bảo người dùng chỉ tải xuống những gì họ thực sự muốn mà không đưa ra những giả định có thể sai lầm về nhu cầu của người dùng.

Tiện ích trò chuyện là một trường hợp sử dụng tuyệt vời khác cho kỹ thuật facade. Hầu hết các tiện ích trò chuyện đều tải xuống một lượng đáng kể JavaScript có thể ảnh hưởng tiêu cực đến quá trình tải trang và khả năng phản hồi đối với hoạt động đầu vào của người dùng. Giống như việc tải bất cứ thứ gì lên trước, chi phí sẽ phát sinh vào thời gian tải, nhưng trong trường hợp tiện ích trò chuyện, không phải người dùng nào cũng không có ý định tương tác với tiện ích này.

Mặt khác, với thành phần tượng trưng, bạn có thể thay thế nút "Start Chat" ("Bắt đầu trò chuyện") của bên thứ ba bằng một nút giả. Sau khi người dùng tương tác có ý nghĩa với tiện ích này (chẳng hạn như giữ con trỏ trên tiện ích trong một khoảng thời gian hợp lý hoặc nhấp vào tiện ích), tiện ích trò chuyện thực tế, có chức năng sẽ được đưa vào vị trí khi người dùng cần.

Mặc dù bạn hoàn toàn có thể tạo các lớp mặt nạ của riêng mình, nhưng vẫn có các lựa chọn mã nguồn mở cho những bên thứ ba phổ biến hơn, chẳng hạn như lite-youtube-embed cho video trên YouTube, lite-vimeo-embed cho video trên Vimeo và React Live Chat Loader cho các tiện ích trò chuyện.

Thư viện tải từng phần JavaScript

Nếu cần tải trì hoãn các phần tử <video>, hình ảnh poster của phần tử <video>, hình ảnh do thuộc tính background-image CSS tải hoặc các phần tử không được hỗ trợ khác, bạn có thể thực hiện việc này bằng giải pháp tải trì hoãn dựa trên JavaScript, chẳng hạn như lazysizes hoặc yall.js, vì việc tải trì hoãn các loại tài nguyên này không phải là một tính năng ở cấp trình duyệt.

Cụ thể, các phần tử <video> tự động phát và lặp lại mà không có bản âm thanh là một lựa chọn thay thế hiệu quả hơn nhiều so với việc sử dụng GIF động. GIF động thường có kích thước lớn hơn gấp nhiều lần so với một tài nguyên video có chất lượng hình ảnh tương đương. Mặc dù vậy, những video này vẫn có thể chiếm băng thông đáng kể, vì vậy, việc tải chúng một cách trì hoãn là một phương pháp tối ưu hoá bổ sung có thể giúp giảm đáng kể lượng băng thông lãng phí.

Hầu hết các thư viện này đều hoạt động bằng Intersection Observer API (và thêm Mutation Observer API nếu HTML của một trang thay đổi sau lần tải ban đầu) để nhận biết thời điểm một phần tử xuất hiện trong khung hiển thị của người dùng. Nếu hình ảnh hiển thị hoặc sắp xuất hiện trong khung hiển thị, thì thư viện JavaScript sẽ thay thế thuộc tính không chuẩn (thường là data-src hoặc một thuộc tính tương tự) bằng thuộc tính chính xác, chẳng hạn như src.

Giả sử bạn có một video thay thế cho ảnh GIF động, nhưng bạn muốn tải video đó một cách trì hoãn bằng giải pháp JavaScript. Bạn có thể làm việc này bằng yall.js với mẫu đánh dấu sau:

<!-- The autoplay, loop, muted, and playsinline attributes are to
     ensure the video can autoplay without user intervention. -->
<video class="lazy" autoplay loop muted playsinline width="320" height="480">
  <source data-src="video.webm" type="video/webm">
  <source data-src="video.mp4" type="video/mp4">
</video>

Theo mặc định, yall.js sẽ quan sát tất cả các phần tử HTML đủ điều kiện có lớp "lazy". Sau khi yall.js được tải và thực thi trên trang, video sẽ không tải cho đến khi người dùng cuộn video vào chế độ xem. Tại thời điểm đó, các thuộc tính data-src trên các phần tử <source> con của phần tử <video> sẽ được thay thế bằng các thuộc tính src. Thao tác này sẽ gửi yêu cầu tải video xuống và tự động bắt đầu phát video.

Kiểm tra kiến thức của bạn

Giá trị mặc định của thuộc tính loading cho cả phần tử <img><iframe> là gì?

"eager"
"lazy"

Khi nào thì bạn nên sử dụng các giải pháp tải từng phần dựa trên JavaScript?

Đối với mọi tài nguyên có thể tải từng phần.
Đối với những tài nguyên không hỗ trợ thuộc tính loading, chẳng hạn như trong trường hợp video tự động phát nhằm thay thế hình ảnh động hoặc để tải hình ảnh áp phích của phần tử <video> một cách trì hoãn.

Khi nào thì mặt tiền là một kỹ thuật hữu ích?

Đối với mọi thành phần nhúng của bên thứ ba, nơi các tài nguyên cần thiết để tải không chỉ đáng kể mà còn có khả năng cao là không phải người dùng nào cũng có thể tương tác với chúng.
Đối với mọi thành phần nhúng của bên thứ ba tiêu thụ một lượng lớn dữ liệu, bất kể nhu cầu của người dùng.

Tiếp theo: Tìm nạp trước và kết xuất trước

Giờ đây, khi đã nắm được cách tải từng phần hình ảnh và các phần tử <iframe>, bạn có thể đảm bảo các trang tải nhanh hơn mà vẫn đáp ứng được nhu cầu của người dùng. Tuy nhiên, có những trường hợp bạn nên tải tài nguyên một cách dự đoán. Trong mô-đun tiếp theo, hãy tìm hiểu về việc tìm nạp trước và kết xuất trước, cũng như cách những kỹ thuật này (khi được sử dụng cẩn thận) có thể tăng tốc đáng kể quá trình điều hướng đến các trang tiếp theo bằng cách tải trước các trang đó.