Giới thiệu thư viện lazysizes.js để lazy load ảnh (phần 2)

Bạn đọc có thể tham khảo phần 1 về thư viện lazysizes ở đây.

JS API

lazysizes tự động nhận diện các phần tử mới với class lazyload, vì thế bạn không cần thực hiện lời gọi hoặc cấu hình bất cứ điều gì trong phần lớn trường hợp.

JS API -- các tùy chọn

Các tùy chọn có thể được thiết lập bằng cách sử dụng tùy chọn cấu hình toàn cục qua khai báo bằng đối tượng có tên lazySizesConfig. Đối tượng này phải được định nghĩa trước đoạn mã của lazysizes. Dưới đây là ví dụ căn bản:

window.lazySizesConfig = window.lazySizesConfig || {};

// sử dụng class .lazy thay vì class .lazyload
window.lazySizesConfig.lazyClass = 'lazy';

// sử dụng data-original thay vì data-src
lazySizesConfig.srcAttr = 'data-original';

//trang được tối ưu hóa tốc độ cho sự kiện onload
lazySizesConfig.loadMode = 1;

Lưu ý nhỏ: sau dấu =, nếu là ký tự thì bạn để trong dấu nháy đơn ”, còn nếu là số thì để nguyên, không thêm gì cả.

Trong trường hợp bạn sử dụng gói module, lời khuyên là thay đổi các tùy chọn trực tiếp sau khi importing module lazysizes:

import lazySizes từ 'lazysizes';
// imports khác...

lazySizes.cfg.lazyClass = 'lazy';

Dưới đây là danh sách các tùy chọn:

  • lazySizesConfig.lazyClass (mặc định: “lazyload”): Tạo class cho tất cả các phần tử cần lazy load (cái này chỉ có thể tạo cho một class. Trong trường hợp bạn cần bổ sung thêm một số phần tử khác, mà đang không có định nghĩa class, đơn giản thêm nó vào từng JS: $(‘.lazy-others’).addClass(‘lazyload’);)
  • lazySizesConfig.preloadAfterLoad (mặc định: false): Để quyết định việc liệu lazysizes có phải tải tất cả các phần tử sau sự kiện window onload hay không. Lưu ý: lazySizes sau đó sẽ tiếp tục tải các ảnh không-trong-tầm-nhìn (not-in-view) bên trong hàng đợi lazy, vì thế các tải xuống khác sau onload không bị chặn.
  • lazySizesConfig.preloadClass (mặc định: “lazypreload”): Tạo class cho các phần tử lazy pre-loaded sau khi onload. Các phần tử này sẽ vẫn được preloaded, ngay cả khi tùy chọn preloadAfterLoad được thiết lập là false. Lưu ý: class này cũng có thể được thiết lập linh hoạt ($currentSlide.next().find(‘.lazyload’).addClass(‘lazypreload’);).
  • lazySizesConfig.loadingClass (mặc định: “lazyloading”): Đây là class sẽ được thêm vào phần tử img sớm nhất có thể khi ảnh bắt đầu tải về. Có thể được sử dụng để thêm các hiệu ứng kiểu unveil.
  • lazySizesConfig.loadedClass (mặc định: “lazyloaded“): Đây là class sẽ được thêm vào bất cứ phần tử nào sớm nhất có thể khi ảnh được tải xong hoặc khi ảnh đi vào tầm nhìn. Có thể được sử dụng để thêm các hiệu ứng unveil hoặc để áp dụng các style.
  • lazySizesConfig.expand (mặc định: 370-500): Tùy chọn expand mở rộng tính toán khung nhìn trình duyệt ra tất cả các hướng, vì thế các phần tử có thể được tải trước khi nó thực sự đi vào khung nhìn trình duyệt. Giá trị mặc định được tính toán dựa trên kích cỡ khung nhìn trình duyệt của thiết bị. Lưu ý: Giá trị hợp lý nằm trong khoảng từ 300 đến 1000, phụ thuộc vào tùy chọn expFactor. Trong trường hợp bạn có nhiều ảnh nhỏ hoặc bạn sử dụng mẫu LQIP bạn có thể để giá trị này nhỏ đi, trong trường hợp bạn có các ảnh kích cỡ lớn hãy thiết lập chỉ số này cao hơn. Cũng cần phải lưu ý là lazySizes sẽ linh động thu nhỏ giá trị này về 0 nếu trình duyệt đang tải xuống và mở rộng nó nếu trình duyệt đang ở trạng thái nghỉ ngơi và người dùng đang không cuộn chuột (bằng cách nhân tùy chọn expand lên 1,5 (expFactor)). Tùy chọn này có thể được ghi đè bằng thuộc tính [data-expand].
  • lazySizesConfig.minSize (mặc định: 40): Cái này được dùng cho tính năng data-sizes=”auto”. Đây là kích cỡ nhỏ nhất của ảnh được dùng để tính toán thuộc tính sizes. Trong trường hợp nó nhỏ hơn giá trị minSize, đoạn mã script sẽ đi qua toàn bộ cây DOM cho đến khi nó tìm được phần tử cha mà có giá trị lớn hơn minSize.
  • lazySizesConfig.srcAttr (mặc định: “data-src”): Đây là thuộc tính sẽ được chuyển thành src
  • lazySizesConfig.srcsetAttr (mặc định: “data-srcset”): Đây là thuộc tính sẽ được chuyển thành srcset.
  • lazySizesConfig.sizesAttr (mặc định: “data-sizes”): Đây là thuộc tính sẽ được chuyển thành sizes.
  • lazySizesConfig.customMedia (mặc định: {}): Tùy chọn đối tượng customMedia là một alias map cho các truy vấn media khác nhau. Nó có thể được sử dụng để tách biệt/tập trung nhiều chỉ định truy vấn triển khai media (layout) của bạn từ thuộc tính source[media] (nội dung/cấu trúc) bằng cách tạo các nhãn truy vấn media (xem thêm tùy biến mở rộng media).
  • lazySizesConfig.loadHidden (mặc định: true): Được dùng vào việc lazySizes có quyết định tải các phần tử ẩn hay không (các phần tử có thuộc tính visibility: hidden). Điều quan trọng cần phải biết là: lazySizes sẽ tải các ảnh bị ẩn theo cách trì hoãn. Nếu bạn muốn chúng tải nhanh nhất có thể, bạn có thể sử dụng opacity: 0.001 nhưng đừng sử dụng visibility: hidden hoặc opacity: 0.
  • lazySizesConfig.ricTimeout (mặc định: 0): Tùy chọn thời gian chờ (timeout) được sử dụng cho requestIdleCallback. Giá trị hợp lý nằm giữa khoảng 0, 100 -- 1000. Giá trị dưới 50 sẽ vô hiệu hóa tính năng requestIdleCallback.
  • lazySizesConfig.throttleDelay (mặc định: 125): Tùy chọn thời gian chờ để điều chỉnh tất cả các listeners. Giá trị hợp lý nằm trong khoảng 66 -- 200.
<script>
window.lazySizesConfig = window.lazySizesConfig || {};
window.lazySizesConfig.customMedia = {
    '--small': '(max-width: 480px)',
    '--medium': '(max-width: 900px)',
    '--large': '(max-width: 1400px)',
};
</script>


<picture>
	<!--[if IE 9]><video style="display: none;><![endif]-->
	<source
		data-srcset="http://placehold.it/500x600/11e87f/fff"
		media="--small" />
	<source
		data-srcset="http://placehold.it/700x300"
		media="--medium" />
	<source
		data-srcset="http://placehold.it/1400x600/e8117f/fff"
		media="--large" />
	<source
        data-srcset="http://placehold.it/1800x900/117fe8/fff" />
    <!--[if IE 9]></video><![endif]-->
    <img

        data-src="http://placehold.it/1400x600/e8117f/fff"
        class="lazyload"
        alt="image with artdirection" />
</picture>

Có lẽ đây là phần nhức đầu nhất trong chuỗi ba phần giới thiệu sử dụng lazySizes, và làm cho những người không thành thạo mã như tôi (người dịch) cảm thấy lúng túng. Tôi sẽ khắc phục chuyện này bằng các đoạn mã thực hành. Dĩ nhiên không sử dụng các tùy chọn này bạn vẫn dùng lazySizes được 50-70% rồi, nhưng nếu ai muốn tùy biến sâu để tối ưu nhiều nhất trong khả năng thì các tùy chọn này không thể không nắm rõ.

Tôi sẽ bắt đầu bằng các ví dụ đơn giản trước:

  • (1) Đổi tên class cần lazy load ảnh

Tôi đổi class ảnh cần lazy load thành lazykiencang bằng đoạn mã đặt trước lazysizes.js như dưới đây:

<script>
window.lazySizesConfig = window.lazySizesConfig || {};

// use .lazy instead of .lazyload
window.lazySizesConfig.lazyClass = 'lazykiencang';
</script>

Tiếp theo tôi đặt class cho các ảnh cần lazy load bằng tên tương ứng:

<img class="lazykiencang" alt="ảnh cá đẹp" data-src="https://kiencang.net/wp-content/uploads/2020/03/anh-ca-dep.jpg">

Trang mẫu: https://code.kiencang.net/lazysizes-change-class.html

Đánh giá về tính năng này: đây là tính năng không quan trọng, tôi nghĩ trong thực tế nó chỉ được dùng khi mà class mặc định lazyload đã bị dùng rồi, hoặc có lẽ phổ biến hơn là trong trường hợp người sử dụng thư viện muốn dùng riêng tên class mà họ thấy tiện dụng. Trong danh sách tùy chọn còn có những cái có tính năng tương tự, chỉ dùng vào việc đổi tên class hoặc thuộc tính, chẳng hạn như lazySizesConfig.loadingClass; lazySizesConfig.loadedClass; lazySizesConfig.srcAttr; lazySizesConfig.srcsetAttrlazySizesConfig.sizesAttr

  • (2) Ví dụ tiếp theo là can thiệp vào tùy chọn expand, cái có tiềm năng cải thiện trải nghiệm người dùng rất lớn thông qua việc tải trước các ảnh khi nó chuẩn bị đi vào khung nhìn trình duyệt với ngưỡng mà nhà phát triển thư viện lazysizes cho là hợp lý là từ 300 -- 1000. Tuy nhiên tôi sẽ thử các tùy chọn cực tiểu và cực đại xem có sự khác biệt nào hay không.

Đầu tiên là expand 100 (tôi đã ghi chú ở phần lưu ý nhỏ, nhưng vẫn nhắc lại ở đây: bạn không được để dấu ” trong giá trị kiểu như ‘100’, nếu không nó sẽ vô hiệu hóa lazy load và tải về tất cả các ảnh):

<script>
window.lazySizesConfig = window.lazySizesConfig || {};

// use expand 100
window.lazySizesConfig.expand = 100;
</script>

Trang mẫu: https://code.kiencang.net/lazysizes-expand100.html

Làm sao bạn biết được mã chạy có đúng hay không và nó khác với mặc định thế nào?

Đơn giản là chúng ta phải thử trong thực tế thôi, hãy xem cách trang tải khi tôi để là expand 100 (với điều kiện kết nối: cột wifi ở xa):

Expand 100 (với điều kiện kết nối: cột wifi ở gần):

Bạn có thể thấy với expand 100 chỉ có 3 ảnh tải về (wifi gần), trong đó tôi chủ động không lazy load ảnh số một vì nó thuộc màn hình đầu tiên (tất cả nội dung thuộc màn hình đầu tiên đều không nên lazy load), điều đó có nghĩa là ngoại trừ hai ảnh tiếp theo, tất cả các ảnh có class lazyload với expand 100 đều không tải lười với độ phân giải màn hình 500 x 500 mà tôi demo ở trên.

P/S: đọc đến đây có thể bạn sẽ thắc mắc wifi ở gần hay ở xa tại sao lại tạo ra số lượng ảnh tải về khác nhau. Wifi ở xa, tốc độ kết nối, lẫn độ ổn định đều kém hơn, do vậy về lý thuyết để tối ưu cho lazy load thì nó nên tải nhiều ảnh hơn so với wifi ở gần. Nhưng ở trên tôi cho là trùng hợp thôi, vì lazysizes như tôi biết chưa tích hợp tiện ích kiểm tra tốc độ kết nối (xem thêm: tải thích ứng). Vậy đâu là lý do? Tôi đoán là do khi ở kết nối chậm, kết xuất hình ảnh ra màn hình sẽ chậm hơn, kết quả là các đoạn mã ảnh sớm xuất hiện trong khung nhìn trình duyệt hơn do các ảnh chưa kịp kết xuất để ẩn nó xuống bên dưới, trong khi trên kết nối nhanh thì ngược lại.

Câu hỏi: tại sao trong phần về expand, tác giả lại khuyên chúng ta nếu dùng ảnh LQIP thì có thể để expand thấp?

Bạn thử nghĩ xem?

1, 2, 3…

Lý do là vì đã có ảnh LQIP giữ chỗ cho ảnh thực rồi, nên ảnh thực có tải chậm một chút cũng không ảnh hưởng nhiều đến trải nghiệm người dùng. Ngoài ra expand có thấp thì quá trình chuyển tiếp từ LQIP sang ảnh chất lượng cao mới dễ quan sát:

Trang demo LQIP (transition 400ms) + expand 100 : https://code.kiencang.net/lazysizes-lqip-expand100.html

Ảnh nhỏ cũng có thể để giá trị expand thấp, vì ảnh nhỏ thì dung lượng cũng nhỏ và nó ít khi bị trễ hơn so với các ảnh lớn.

OK, đây là lúc bạn chuyển sự tập trung sang ví dụ tiếp.

Tiếp theo là expand 1000:

<script>
window.lazySizesConfig = window.lazySizesConfig || {};

// use expand 1000
window.lazySizesConfig.expand = 1000;
</script>

Trang mẫu: https://code.kiencang.net/lazysizes-expand1000.html

Kết quả tải thực tế (wifi gần):

expand 1000 wifi gần

Khi expand đươc mở rộng lên 1000 thì có đến 14 ảnh được tải chứ không chỉ 3 ảnh như khi để expand là 100. Điều này khớp với dự đoán của chúng ta.

Cuối cùng expand 10000 (tôi dự đoán là tất cả ảnh sẽ được tải về):

<script>
window.lazySizesConfig = window.lazySizesConfig || {};

// use expand 10000
window.lazySizesConfig.expand = 10000;
</script>

Trang mẫu: https://code.kiencang.net/lazysizes-expand10000.html

Kết quả tải về (wifi gần):

lazysizes expand 10000 wifi gần

Đúng như dự đoán khi expand mở rộng đến 10 ngàn, tất cả ảnh lazy load sẽ tải ngay lúc ban đầu (trang demo có 31 ảnh với độ dài khoảng 10 ngàn pixel).

Đánh giá về tính năng này: tôi cho rằng expand là tính năng quan trọng, việc mở rộng khung nhìn trình duyệt để tải trước chắc chắn đem đến tính khả dụng cao hơn trên các thiết bị có kết nối chậm. Bởi vì trên thiết bị có kết nối nhanh, phần cứng tốt, expand có giá trị nhỏ không ảnh hưởng gì cả, trang vẫn sẽ tải tốt, không gây hiện tượng chậm trễ. Nhưng trên kết nối chậm, việc mở rộng expand sẽ tránh được rủi ro ảnh không kịp tải ảnh, vì nó có thời gian tải về dài hơn. Đây là lý do mà một số plugin lazy load cho WordPress tích hợp tính năng tương tự, chẳng hạn như Flying Images, họ thậm chí có tùy chọn cho phép mở rộng đến 3000px tính từ đáy thiết bị của người dùng:

tải trước ảnh khi nó đi vào khung nhìn trình duyệt
  • (3) Ví dụ này sẽ can thiệp vào tùy chọn lazySizesConfig.preloadAfterLoad. Nếu đúng như tôi nghĩ thì trong nhiều trường hợp đây là tùy chọn mà bạn nên để true, để ngay khi trình duyệt rảnh rỗi, các ảnh lazy load sẽ được tải về và dùng được luôn khi cần thiết. Tuy nhiên đây chỉ là dự đoán, tôi sẽ thử trong thực tế xem tình hình thế nào.
<script>
window.lazySizesConfig = window.lazySizesConfig || {};

// preload sau khi tải
lazySizesConfig.preloadAfterLoad = 'true';
</script>

Trang mẫu: https://code.kiencang.net/lazysizes-preload-after-load.html

Quả thực giống như tôi đoán, sau khi tải một số ảnh đủ điều kiện kích hoạt, nếu thấy trình duyệt bận, các ảnh lazy load còn lại sẽ không được tải, còn nếu trình duyệt rảnh, lazysizes sẽ tải tất cả các ảnh tải lười còn lại mà không phải đợi người dùng cuộn chuột đến gần. Để mô tả tính năng này, tôi đã quay video màn hình. Trong đó bạn sẽ thấy khi tôi di chuyển chuột (biểu tượng hình tròn di chuyển, tương đương với trình duyệt bận rộn) thì lazysizes chỉ tải các ảnh theo mặc định về (từ lúc bắt đầu đến giây 24 là trong quá trình tải, từ giây 24 đến giây 30 quá trình tải đã xong nhưng tôi vẫn di chuyển chuột nên không có preload), còn nếu tôi dừng cuộn trỏ chuột (hình tròn đứng yên, tương đương với trình duyệt rảnh rỗi, từ giây 30 trở đi) thì lazysizes sẽ tải toàn bộ ảnh còn lại về:

lazysizes preload after load

Đánh giá về tính năng này: chắc chắn cần nhiều trải nghiệm hơn để đánh giá chính xác, vì tôi còn một số hoài nghi. Tuy nhiên dựa trên logic thì tôi đánh giá cao tính năng này, thậm chí là rất quan trọng. Khi trang tải và xuất được hình ảnh đầu tiên, người dùng thường cuộn chuột (máy bàn) hoặc vuốt màn hình (di động) để bắt đầu tiêu thụ nội dung, và các ảnh cần lazy load chưa được kích hoạt sẽ không tải về. Và khi người dùng đến phần nội dung hấp dẫn, dừng lại để đọc hoặc xem, trình duyệt trong trạng thái rảnh và lazysizes sẽ tải các ảnh còn lại nhanh nhất có thể. Nghĩa là đây là tính huống mà ảnh được kích hoạt tải dựa trên việc trình duyệt có rảnh rỗi hay không, chứ không đơn thuần chỉ dựa vào khoảng cách giữa ảnh và viewport nữa, đây là cách hạn chế tối đa trạng thái trễ (ảnh không kịp tải). Dĩ nhiên đây không phải là cách tiết kiệm băng thông. Lazy load thông thường sẽ tiết kiệm băng thông không nhiều thì ít, vì có một tỷ lệ lớn người dùng không cuộn chuột sâu xuống dưới trang, và ảnh ở những khu vực đó sẽ không tải. Nhưng kiểu lazy load này sẽ tận dụng mọi lúc trình duyệt rảnh để tải ảnh về, và trong hầu hết trường hợp ảnh sẽ được tải về hết trong vòng vài giây khi người dùng không thao tác gì (trừ khi họ đóng trình duyệt rất nhanh khi vừa mới vào trang). Có lẽ cách này không phù hợp với các trang có lưu lượng truy cập lớn, và giá trị visitor không cao, vì lúc ấy băng thông sẽ là vấn đề. Ngược lại các trang đặt trọng tâm cao nhất vào khả năng hiển thị nội dung cho người dùng rất có thể nên áp dụng.

  • (4) class lazypreload, tương tự như tính năng preloadAfterLoad, nhưng nếu preloadAfterLoad tác động đến toàn bộ ảnh thì lazypreload chỉ tác động đến ảnh nào có gắn class này. Giờ tôi sẽ thử nó xem thế nào, cũng chẳng cần phải đổi tên class tải trước ảnh này làm gì do vậy bạn sẽ không phải thêm đoạn mã window.lazySizesConfig. Cách thử là tôi sẽ đặt class này vào mấy ảnh cuối trong demo, xem lúc trình duyệt rảnh các ảnh này có được tải về không (lazy load mặc định thì nó không vì vẫn chưa đủ điều kiện kích hoạt / đạt đến ngưỡng khoảng cách so với viewport).

Mã mẫu:

<img width="640" data-src="https://kiencang.net/wp-content/uploads/2020/03/anh-nang.jpg" class="lazyload lazypreload" />

Trang mẫu: https://code.kiencang.net/lazysizes-class-lazypreload.html

Khi bạn dùng công cụ Chrome DevTools để kiểm tra, bạn sẽ thấy 3 ảnh cuối gắn class lazypreload được tải về mặc dù nó ở vị trí cuối cùng trên trang:

ảnh có class lazypreload được tải về sơm

Đánh giá về tính năng này: là một tính năng hay, vì có thể bạn chỉ muốn preload một số ảnh thôi, chứ không muốn sử dụng tính năng preloadAfterLoad để tải về tất cả các ảnh khi trình duyệt rảnh rỗi.

Chúng ta sẽ tìm hiểu các tùy chọn còn lại:

  • lazySizesConfig.expFactor (mặc định: 1.5): expFactor được sử dụng để tính toán “mở rộng preload”, bằng cách nhân giá trị expand thông thường với expFactor, cái được sử dụng để preload các tài nguyên khi trình duyệt ở trạng thái nghỉ ngơi (idling, nghĩa là không có lưu lượng truy cập mạng quan trọng và không cuộn chuột). Giá trị hợp lý nằm giữa khoảng 1,54, tùy thuộc vào tùy chọn expand. Bạn sẽ hiểu rõ hơn ý nghĩa của nó khi đọc đến phần loadMode.
  • lazySizesConfig.hFac (mặc định: 0.8): hFac (yếu tố bề ngang) sửa đổi mở rộng chiều ngang bằng cách nhân giá trị expand với giá trị hFac. Trường hợp sử dụng trong thực tế: trong trường hợp băng chuyền ảnh (carousels) bạn thường có mong muốn mở rộng chiều ngang hẹp, hơn là tùy chọn mở rộng chiều cao. Giá trị hợp lý nằm giữa 0,4 -- 1. Trong trường hợp bạn không chắc chắn về website cuộn theo chiều ngang, bạn có thể sử dụng giá trị 1 -- 1,5.
  • lazySizesConfig.loadMode (mặc định: 2): loadMode có thể được sử dụng để hạn chế các chế độ tải cho phép. Các giá trị có thể bao gồm 0 = không tải bất cứ thứ gì, 1 = chỉ tải các phần tử nằm trong khung nhìn trình duyệt, 2 = tải cả các phần tử nằm gần khung nhìn trình duyệt (với tùy chọn expand) và 3 = tải cả các phần tử nằm ở xa (với ngưỡng tùy chọn expand * expFactor). Giá trị này được tự động thiết lập là 3 sau onload. Thay đổi giá trị này thành 1 nếu bạn đã tối ưu hóa cho sự kiện onload hoặc thay đổi nó thành 3 nếu sự kiện onload của bạn đã bị trì hoãn rất nhiều.
  • lazySizesConfig.init (mặc định: true): Theo mặc định thì lazysizes sẽ tự khởi tạo bản thân nó, để tải tài nguyên sớm nhất có thể. Trong trường hợp không chắc chắn bạn cần thiết lập/cấu hình một vài thứ với đoạn mã sau, bạn có thể thiết lập tùy chọn này là false và gọi lazySizes.int() rõ ràng sau.

Ví dụ demo khi loadMode = 1, trang mẫu (bạn nên test trong khi ở gần cột phát wifi): https://code.kiencang.net/lazysizes-loadmode1.html

expFactor là tùy chỉnh có nhiều điểm giống preloadAfterLoad, nhưng nó là phiên bản preload bớt “gắt”:

  • Trong thời gian rảnh rỗi lazysizes sẽ tải các ảnh nằm trong viewport mở rộng, chẳng hạn nếu bạn chọn expand là 1000, và expFactor là 6, lúc rảnh rỗi trình duyệt sẽ mở rộng viewport đến 4000, và do vậy rất nhiều ảnh có khả năng được tải
  • Trong khi với preloadAfterLoad, khi trình duyệt rảnh rỗi, nó sẽ tải tất cả các phần tử ảnh càng nhanh càng tốt, không kể ngưỡng nào cả

Trang demo với expand = 1000 và expFactor = 6: https://code.kiencang.net/lazysizes-exp1000-factor6.html

(Đọc tiếp phần 3)

Leave a Comment