Hướng dẫn lazy load scroll javascript
Bài viết được sự cho phép của tác giả: Lương Văn Phúc Show
Lời tựaTrong bài viết lần này, hãy cùng mình nghiên cứu từ “lịch sử” cho đến “hiện đại” các vấn đề về lazy-loading images nhé (có cả lý thuyết và code). Những trình bày của mình dựa trên kinh nghiệm và kiến thức của bản thân, cộng thêm nghiên cứu và tổng hợp từ nhiều nguồn khác nhau nhằm cung cấp cho các bạn cái nhìn đầy đủ nhất cả về lý thuyết lẫn thực hành. Nếu các bạn có góp ý hay bổ sung gì thì hãy để lại bình luận bên dưới nhé. Kĩ thuật “lazy-loading images” hiện nay đã có nhiều cải tiến so với trước, tuy nhiên mình sẽ chưa đi ngay vào code mà sẽ tản mạn về lý thuyết một tí. Bạn nào muốn nhảy nhanh đến phần thực hành thì có thể bỏ qua phần chém gió dài dòng của mình nhé. Lý thuyết: Lazy loading là gì?“Trì hoãn” việc tải các tấm ảnh khuất màn hình. Ảnh addyosmani.com Mình bổ sung phần này dành cho các bạn mới (fresh). Trước khi ứng dụng một công nghệ hay kĩ thuật gì vào dự án, các bạn nên hiểu về lý thuyết, thuật ngữ liên quan và lý do sử dụng nhé, vì biết đâu bạn đang sử dụng “dao mổ trâu để giết gà” đấy. Lazy loading: có một điều quan trọng các bạn cần hiểu là nó chỉ là một khái niệm (ý tưởng) thôi nhé. Nghĩa là nó có thể được sử dụng trong nhiều trường hợp khác nhau, nhiều ngôn ngữ lập trình khác nhau, và mỗi nơi lại có nhiều cách implement khác nhau, nhưng ý tưởng thì cũng tương đối giống nhau thôi. Ví dụ như trong Angular thì có lazy-loading modules… Mình xin lấy một ví dụ mà chắc ai cũng đã từng gặp, hãy tưởng tượng rằng bạn bước vào một quán nhậu:
Bạn có thật sự mong đợi điều trên không? Có một cách “tốt” hơn trong trường hợp này:
Lazy loading chính là vậy đó. Khi user vào một trang web có rất nhiều feature (ảnh), nhiều khi họ chỉ vào mỗi trang chủ dạo chơi thôi. Và trong trang chủ, họ cũng chỉ xem cái giao diện đầu tiên chứ chưa chắc đã scroll xuống và xem hết. Trong trường hợp đó, tại sao chúng ta lại bắt browser tải về tất cả mọi feature, tất cả mọi tấm ảnh trong khi user chưa cần hoặc có thể không cần đến? Trong giới hạn của bài viết này, mình xin phép chỉ phân tích về kĩ thuật lazy-loading images thôi nhé. Tại sao lại phải “lazy-loading images”?Giải quyết vấn đề về performanceKhi bạn viết code 100 tấm ảnh chứa trong các thẻ Tiết kiệm tài nguyênViệc “trì hoãn” những resource chưa cần thiết giúp tiết kiệm CPU, GPU, bộ nhớ, băng thông… đặc biệt là trên các thiết bị di động có tốc độ kết nối chậm. Tăng giải nghiệm người dùngNgày nay số lượng người dùng trên mobile đã vượt qua cả trên desktop. Nếu trang của bạn có quá nhiều hình ảnh gây chậm tốc độ tải trang, thì user sẽ cảm thấy rằng đó là một trang web tệ và không còn muốn quay lại lần sau. Tăng điểm số trên các trang đo tốc độ web, ví dụ như Pagespeed InsightsNếu bạn quan tâm đến “điểm số” của web thì lazy load sẽ giúp điểm số tăng cao hơn nhé (nếu bạn áp dụng đúng cách). Bên cạnh đó, tốc độ web và trải nghiệm người dùng ảnh hưởng rất lớn đến xếp hạng (ranking) trang web trên Google search nữa nhé. Mặc dù lazy-load có rất nhiều lợi ích như trên, bạn cần đặt câu hỏi cho bản thân liệu bạn có thật sự cần nó không, vì “lazy-loading images” cũng có những tác hại (mình có chia sẻ ở cuối bài viết) nếu chúng ta lạm dụng hoặc sử dụng sai cách. Vậy khi nào thì chúng ta nên lazy-load hình ảnh của web:
Trước khi “lazy-load”, chúng ta cần phải làm gì?Trước khi áp dụng bất kì kĩ thuật “lazy-load images” nào, còn nhiều việc mà bạn phải làm trước đó:
Bản chất của “lazy-loading images” rất đơn giảnBạn nói với browser rằng: “Này browser, tao có các tấm ảnh này, nhưng mày đừng tải nó nhé. Khi nào user scroll đến vị trí tấm ảnh nào, thì mày hãy tải tấm ảnh ấy ngay”. Có 3 vấn đề cốt lõi mà mình tô đậm trong đoạn hội thoại trên. 1. Đừng tải: Khi bạn gán thuộc tính
2. scroll đến vị trí tấm ảnh: để kiểm tra được điều này, developers thường dùng những thứ gọi là “trick” như sự kiện 3. tải tấm ảnh: để bắt browser tải tấm ảnh, chúng ta chỉ đơn giản là copy tấm ảnh từ
Lazy-load ảnh background cũng tuân theo các bước như trên, chỉ là thay vì copy từ Kĩ thuật 1: lazy-load chính thống và hiện đạiChém gió hơi nhiều rồi, mình đi vào code ngay nhé. Thay vì đi ngược từ cách “cổ xưa” cho đến
cách “hiện đại”, mình xin giới thiệu về cách hiện đại trước. Cách này là cách chính thống (nghĩa là nó không phải trick), bạn sẽ không cần bất kì dòng Javascript nào, hay bất kì config gì phức tạp. Điều duy nhất bạn làm là báo cho browser biết tấm ảnh nào cần lazy-load là xong, với thuộc tính
Thuộc
tính
Một điều quan trọng bạn cần lưu ý là nếu bạn muốn lazy-load ảnh background, thì cách này sẽ không chạy đâu nhé. Thay vào đó bạn phải dùng 2 cách mà mình đề cập bên dưới. Một lưu ý khác nữa, là browser không đợi đến lúc tấm ảnh xuất hiện ở viewport mới tải, mà khi tấm ảnh gần xuất hiện ở viewport là nó đã tải rồi nhé. Điều này giúp browser tải tấm ảnh sớm nhất có thể để nó sẵn sàng xuất hiện trên màn hình kịp lúc, vì tải cũng mất thời gian mà. Nếu bạn muốn tìm hiểu thêm thì có thể search từ khóa “threshold” nhé. Theo trang Can I Use, thuộc tính này đã được hỗ trợ trên Chrome và Firefox, không hỗ trợ IE, còn Safari thì đang thử nghiệm và sẽ sớm có thôi. FallbackTrong trường hợp browser chưa support thuộc tính
Nguồn: https://addyosmani.com/blog/lazy-loading/ Kĩ thuật 2: lazy-load sử dụng Intersection Observer APIĐịnh nghĩa về “Intersection Observer API” thì mình không nêu ra ở đây vì nó nằm ngoài phạm vi bài viết. Ứng dụng của nó thì có rất nhiều, và “lazy-loading images” là một trong số những ứng dụng ấy. Nói nôm na, bạn sử dụng API này để biết được khi nào user scroll đến vị trí tấm ảnh, và khi ấy chúng ta “ép” browser tải tấm ảnh ngay lập tức. Tuy sử dụng Intersection Observer cho việc lazy-load là một cách không quá “lỗi thời”, nó vẫn không được support trên IE nhé. Tuy nhiên hầu hết các browser hiện nay và cả mobile nữa đều support nó, nên có thể nói đây là cách khá “an toàn”. Nếu vì tính chất công việc mà bạn buộc phải support IE, bạn có thể sử dụng polyfill để giả lập Intersection Observer API, hoặc sử dụng kĩ thuật số 3 tiếp theo đây. Kĩ thuật này là khá cổ điển, có thể xem nó là giải pháp cuối cùng để lazy-load images nếu bạn buộc phải support các browser cũ. Giải pháp này tuy chạy nhưng có nhiều vấn đề về performance và hiệu ứng giật (lag), vì nó là cách thủ công mà. Bạn sẽ phải tính toán xem vị trí của tấm ảnh xem nó đã xuất hiện ở trên màn hình chưa, và cứ lặp đi lặp lại mỗi khi user scroll. Mình không khuyến khích các bạn sử dụng cách này cho production nhé. Nếu buộc phải support các browser cũ, thì polyfill của Intersection Observer ở trên là đủ rồi. “Lazy-loading images” có hại không?Mặc dù lazy-load là một kĩ thuật tốt và hiệu quả, việc sử dụng lazy-loading images sẽ có một số mặt hại sau: 1. Page bị nhảy khi tấm ảnh được load (layout shift)Vấn đề này là thường gặp nhất khi áp dụng lazy-load. Lý do là vì browser không biết được kích thước của tấm ảnh trước khi nó thật sự load nó. Vì thế khi load xong tấm ảnh, nó sẽ đẩy content xung quanh ra xa gây giật trang khá khó chịu. Có rất nhiều cách để giải quyết vấn đề này, nhưng để tránh bài viết quá dài nên mình chỉ đưa một cách đơn giản và dễ hiểu nhất thôi. Nếu các bạn cần tìm hiểu thêm nhiều cách khác thì có thể Google hoặc để comment bên dưới nhé. Cách để tránh layout shift thường thấy là chỉ ra chính xác kích thước của tấm ảnh, khi đó browser sẽ dành chỗ trước cho tấm ảnh đó trên layout, để đảm bảo khi load xong tấm ảnh, nó sẽ lấp đúng khoảng trống đó và không gây lỗi layout shift.
Ngoài ra, bạn nên tránh lazy-load những tấm ảnh ở ngay phần đầu của trang (thuật ngữ chuyên môn gọi là above-the-fold) để tránh việc layout shift nhé. 2. Khi web bị tắt JavascriptĐối với những user đã tắt Javascript trên trình duyệt (tỉ lệ rất thấp), thì cách sử dụng Intersection Observer hay
Tuy nhiên bạn không cần phải lo lắng vì thật sự tỉ lệ user lướt web mà tắt javascript là rất thấp. 3. Không tốt cho SEOVẫn là khi sử dụng Intersection Observer hay
4. Thêm code Javascript chỉ để lazy load vài tấm ảnhNếu số lượng ảnh cần lazy load chỉ dưới 5 tấm, và chúng không ảnh hưởng nhiều đến tốc độ tải trang, thì mình khuyên không nên sử dụng lazy load. Điều này chỉ khiến bạn tốn thêm nhiều dòng code javascript, và sẽ lớn hơn nếu bạn nhúng cả polyfill vào nữa. Thay vào đó, hãy tìm cách optimize tấm ảnh của bạn, và sử dụng native lazy-load nếu có thể. 5. |
Native | Intersection Observer | scroll event handler | |
---|---|---|---|
Performance | Tốt nhất | Tốt | Không quá tệ nếu xử lý tốt |
Background Image | Không | Có | Có |
Javascript | Không cần | Cần | Cần |
SEO | Không ảnh hưởng | Có ảnh hưởng | Có ảnh hưởng |
User friendly | Tốt nhất | Tốt | Có thể giật nếu xử lý không tốt |
Browser support | Chưa nhiều (thiếu safari) Không support IE | Tốt trên các browser Không support IE | Tốt trên hầu hết browser, kể cả IE |
Polyfill | Có | Có | Không cần (bản thân nó đã là một polyfill) |
Download Source codes
Mình đính kèm source code đơn giản cho 3 giải pháp ở trên, bạn nào cần thì có thể tải về để tham khảo nhé.
https://drive.google.com/file/d/16jTYVUiA5xykDrhaP60GwvuztOPTOhwB/view?usp=sharing
Kết luận
“Lazy-loading images” chỉ là một trong rất nhiều kĩ thuật để tối ưu performance cho web. Sắp tới mình sẽ viết thêm nhiều bài viết phân tích về web performance nhé.
Bài viết của mình khá dài rồi, hi vọng các bạn thích bài viết lần này của mình, mình sẽ luôn cập nhật để nó không bị lỗi thời nhé. Nếu các bạn có góp ý hay thắc mắc gì thì xin để lại comment bên dưới.
Bài viết gốc được đăng tải tại grab-cv.com
Có thể bạn quan tâm:
- Bản chất của Lazy Loading Images
- Hướng dẫn lazy load component trong React
- Kiểm tra element có nằm trong viewport không bằng javascript
Xem thêm các việc làm JavaScript hấp dẫn tại TopDev