Tối ưu hoá hình ảnh bằng Chỉ thị hình ảnh góc

Kara Erickson
Kara Erickson
Leena Sohoni
Leena Sohoni

Vào tháng 5 năm 2022, nhóm Aurora và Angular đã thông báo rằng họ sẽ cộng tác trên một lệnh hình ảnh cho Angular. Gần đây, chúng tôi đã phát hành hướng dẫn này cho bản dùng thử dành cho nhà phát triển trong Angular phiên bản 14.2. Bài đăng này trình bày cách lệnh hình ảnh mới, NgOptimizedImage, hỗ trợ tối ưu hoá hình ảnh trong Angular.

Thông tin khái quát

Hình ảnh là một thành phần phổ biến và quan trọng trong trải nghiệm người dùng trên web, với 99,9% trang web tạo ra yêu cầu cho một hoặc nhiều hình ảnh. Hình ảnh cũng là yếu tố đóng góp đáng kể nhất vào kích thước trang, chiếm trung bình 982 kilobyte mỗi trang.

Do số lượng và kích thước ngày càng tăng, hình ảnh có thể cản trở hiệu suất của trang web và ảnh hưởng đến các chỉ số Các chỉ số quan trọng về trang web. Đối với 79,4% trang dành cho máy tính, hình ảnh là phần tử Thời gian hiển thị nội dung lớn nhất (LCP) trong năm 2021. Do đó, việc theo đuổi hình ảnh được tối ưu hoá đã trở thành một nỗ lực không ngừng của nhiều người.

Nhóm Aurora tin tưởng vào việc tận dụng sức mạnh của các khung để cung cấp các giải pháp tích hợp sẵn cho các thách thức thường gặp của nhà phát triển. Bước đầu tiên của họ trong lĩnh vực tối ưu hoá hình ảnh là thành phần hình ảnh Next.js. Họ coi thành phần này là một nền tảng thử nghiệm để xem việc cải thiện trải nghiệm nhà phát triển (DX) về việc tối ưu hoá hình ảnh có thể giúp tăng hiệu suất cho nhiều ứng dụng sử dụng khung hơn hay không.

Tập hợp kết quả đầu tiên từ người dùng Next.js Leboncoin rất đáng khích lệ. Leboncoin đã cải thiện đáng kể chỉ số LCP (từ 2,4 giây xuống còn 1,7 giây) sau khi bắt đầu sử dụng next/image. Việc sử dụng next/image sau đó trong cộng đồng đã góp phần làm tăng số lượng gốc Next.js đáp ứng ngưỡng LCP. Chẳng bao lâu sau, đã có yêu cầu về các tính năng tương tự trong các khung khác, một trong số đó là Angular.

Do đó, Aurora đã tham khảo ý kiến của Angular và Nuxt để tạo nguyên mẫu thành phần hình ảnh cho các khung này. Thành phần hình ảnh Nuxt được phát hành vào năm ngoái. Giờ đây, lệnh hình ảnh Angular (NgOptimizedImage) đã được phát hành để đưa các tuỳ chọn tối ưu hoá hình ảnh mặc định vào Angular.

Cơ hội

Angular là một trong những khung JavaScript hàng đầu mà các nhà phát triển sử dụng hiện nay. Hơn 50 nghìn nguồn gốc được HTTPArchive thu thập thông tin trên thiết bị di động và có gần 3 triệu lượt tải xuống hằng tuần trên NPM.

LCP cho các trang web Angular trong vòng 1 năm qua.

Khi xem xét điểm số của Chỉ số quan trọng chính của trang web, tỷ lệ phần trăm các nguồn gốc Angular đáp ứng ngưỡng LCP "tốt" vẫn cần được cải thiện. Chỉ 18,74% trang web Angular có LCP tốt trên thiết bị di động vào tháng 6 năm 2022. Vì hình ảnh là phần tử LCP của hơn 70% trang web trên thiết bị di động và máy tính, nên hình ảnh LCP chưa được tối ưu hoá có thể là một trong những nguyên nhân chính khiến LCP kém hơn trên các trang web Angular.

Chỉ thị hình ảnh Angular được thiết kế để giúp cải thiện những con số này.

MVP cho chỉ thị NgOptimizedImage

MVP của lệnh hình ảnh Angular được xây dựng dựa trên các bài học từ các thành phần hình ảnh mà Aurora đã xây dựng cho đến nay, đồng thời điều chỉnh thiết kế cho phù hợp với trải nghiệm kết xuất phía máy khách của Angular. Nhiều vấn đề tối ưu hoá hình ảnh tiêu chuẩn đã được giải quyết bằng cách:

  • Cung cấp các giá trị mặc định mạnh.
  • Gửi lỗi hoặc cảnh báo để đảm bảo tuân thủ các phương pháp hay nhất.

Sau đây là những điểm nổi bật của thiết kế:

  1. Tải từng phần thông minh

    Hình ảnh mà người dùng không nhìn thấy khi tải trang (ví dụ: hình ảnh bên dưới màn hình đầu tiên hoặc hình ảnh băng chuyền bị ẩn) nên được tải lười. Tính năng tải lười giải phóng tài nguyên trình duyệt để tải văn bản, nội dung nghe nhìn hoặc tập lệnh quan trọng khác. Hầu hết hình ảnh không quan trọng và nên được tải lười, nhưng chỉ 7,8% số trang sử dụng tính năng tải lười gốc trong năm 2021.

    Theo mặc định, lệnh hình ảnh Angular tải lười hình ảnh không quan trọng và chỉ tải nhanh hình ảnh được đánh dấu đặc biệt là priority. Điều này đảm bảo hầu hết hình ảnh đều có hành vi tải tối ưu.

  2. Ưu tiên hình ảnh quan trọng

    Thêm gợi ý tài nguyên (ví dụ: preload hoặc preconnect) để ưu tiên tải các hình ảnh quan trọng là một phương pháp hay nhất được đề xuất. Tuy nhiên, hầu hết các ứng dụng đều không sử dụng các tính năng này. Theo Web Almanac 2021, chỉ 12,7% số trang dành cho thiết bị di động sử dụng gợi ý kết nối trước và chỉ 22,1% số trang dành cho thiết bị di động sử dụng gợi ý tải trước.

    Chỉ thị hình ảnh hoạt động trên hai mặt khi hình ảnh được đánh dấu là ưu tiên.

    • Phương thức này đặt fetchpriority của hình ảnh thành "high" để trình duyệt biết rằng nó phải tải hình ảnh xuống với mức độ ưu tiên cao.
    • Ở chế độ phát triển, quy trình kiểm tra thời gian chạy xác nhận rằng gợi ý tài nguyên preconnect đã được đưa vào tương ứng với nguồn gốc của hình ảnh.

    Ở chế độ phát triển, lệnh này cũng sử dụng PerformanceObserver API để xác minh rằng hình ảnh LCP đã được đánh dấu là priority như dự kiến. Nếu không được đánh dấu là priority, hệ thống sẽ gửi một lỗi hướng dẫn nhà phát triển thêm thuộc tính priority vào hình ảnh LCP.

    Cuối cùng, sự kết hợp giữa tính năng tự động hoá và tuân thủ này đảm bảo rằng hình ảnh LCP có gợi ý preconnect, giá trị thuộc tính fetchpriorityhigh và không được tải lười.

  3. Cấu hình được tối ưu hoá cho các công cụ xử lý hình ảnh phổ biến

    Bạn nên sử dụng CDN hình ảnh cho các ứng dụng Angular. Các CDN này thường cung cấp dịch vụ tối ưu hoá theo mặc định.

    Chỉ thị này khuyến khích việc sử dụng CDN hình ảnh bằng cách cung cấp trải nghiệm nhà phát triển (DX) đặc biệt hấp dẫn để định cấu hình CDN trong ứng dụng. Chỉ thị này hỗ trợ API trình tải cho phép bạn xác định nhà cung cấp CDN và URL cơ sở trong cấu hình. Sau khi định cấu hình, bạn chỉ cần xác định tên thành phần trong mã đánh dấu. Ví dụ:

    // in module providers:
    provideImgixLoader('https://mysite.net/assets/')
    
    // in markup
    <img ngSrc="image.png" >
    <img ngSrc="image2.png" >
    

    Điều này tương đương với việc thêm các thẻ hình ảnh sau và giảm lượng mã đánh dấu mà nhà phát triển phải thêm cho mỗi hình ảnh.

    <img src="https://mysite.net/assets/image.png">
    <img src="https://mysite.net/assets/image2.png">
    

    Chỉ thị hình ảnh cung cấp trình tải tích hợp sẵn với cấu hình tối ưu cho các CDN hình ảnh phổ biến nhất. Các trình tải này sẽ tự động định dạng URL hình ảnh để đảm bảo rằng bạn sử dụng chế độ cài đặt nén và định dạng hình ảnh được đề xuất cho từng CDN.

  4. Lỗi và cảnh báo tích hợp sẵn

    Ngoài các tính năng tối ưu hoá tích hợp sẵn nêu trên, lệnh này cũng có các tính năng kiểm tra tích hợp sẵn để đảm bảo nhà phát triển đã tuân thủ các phương pháp hay nhất được đề xuất trong mã đánh dấu hình ảnh. Chỉ thị hình ảnh thực hiện các bước kiểm tra sau.

    1. Hình ảnh không có kích thước: Lệnh hình ảnh sẽ gửi lỗi nếu mã đánh dấu hình ảnh không xác định rõ chiều rộng và chiều cao. Hình ảnh chưa được định cỡ có thể gây ra thay đổi bố cục, ảnh hưởng đến chỉ số Mức thay đổi bố cục tích luỹ (CLS) của trang. Phương pháp hay nhất được đề xuất để ngăn chặn điều này là hình ảnh phải có các thuộc tính widthheight được chỉ định.

    2. Tỷ lệ khung hình: Chỉ thị hình ảnh sẽ gửi một lỗi để cho nhà phát triển biết nếu tỷ lệ khung hình của width:height được xác định trong HTML không gần với tỷ lệ khung hình thực tế của hình ảnh đã kết xuất. Điều này có thể khiến hình ảnh bị méo trên màn hình. Điều này có thể xảy ra nếu

      1. Bạn đã xác định sai kích thước (chiều rộng hoặc chiều cao) do nhầm lẫn hoặc
      2. Nếu bạn đã xác định một kích thước theo tỷ lệ phần trăm trong CSS nhưng không xác định kích thước còn lại (ví dụ: width: 100% cần height: auto để đảm bảo hình ảnh tăng theo cả hai kích thước).
    3. Hình ảnh quá khổ: Nếu hình ảnh không xác định srcset và hình ảnh nội tại lớn hơn đáng kể so với hình ảnh được kết xuất, thì lệnh này sẽ hiển thị cảnh báo đề xuất sử dụng các thuộc tính srcsetsizes.

    4. Mật độ hình ảnh: Chỉ thị này sẽ gửi lỗi nếu bạn cố gắng đưa một hình ảnh vào srcset có mật độ điểm ảnh lớn hơn 3x. Bạn thường không nên sử dụng các chỉ số mô tả cao hơn 2x vì điều này có thể dẫn đến hậu quả ngoài mong muốn là buộc các thiết bị di động có độ phân giải cao phải tải hình ảnh khổng lồ xuống. Hơn nữa, mắt người thực sự không thể phân biệt được nhiều sự khác biệt khi tỷ lệ trên 2x.

Thử thách

Việc điều chỉnh các chiến lược tối ưu hoá hình ảnh để hoạt động trong khung phía máy khách là thách thức chính khi thiết kế NgOptimizedImage. Trải nghiệm kết xuất mặc định trên Next.js là Kết xuất phía máy chủ (SSR) hoặc Tạo trang web tĩnh (SSG), còn trên Angular là Kết xuất phía máy khách (CSR). Mặc dù Angular hỗ trợ thư viện SSR – angular/universal – nhưng hầu hết các ứng dụng Angular (~60%) đều sử dụng CSR.

Chỉ thị hình ảnh được tạo hoàn toàn cho CSR để phù hợp với trường hợp sử dụng thông thường trong các ứng dụng Angular. Điều này đặt ra thêm các quy tắc ràng buộc và nhóm phải suy nghĩ lại về cách xây dựng các phương pháp tối ưu hoá cụ thể cho các ứng dụng CSR.

Sau đây là một số thách thức mà chúng tôi gặp phải:

  1. Hỗ trợ gợi ý tài nguyên

    Tính năng tải trước các thành phần quan trọng giúp trình duyệt phát hiện các thành phần đó sớm hơn. Tuy nhiên, việc đưa gợi ý tài nguyên vào ứng dụng Angular sẽ phức tạp vì:

    Thêm theo cách thủ công: Nhà phát triển khó có thể thêm gợi ý tài nguyên preload theo cách thủ công. Angular sử dụng một tệp index.html dùng chung cho toàn bộ dự án hoặc cho tất cả các tuyến trong trang web. Do đó, <head> của tài liệu giống nhau cho mọi tuyến đường (ít nhất là tại thời điểm phân phát). Việc thêm bất kỳ gợi ý preload nào vào <head> có nghĩa là tài nguyên sẽ được tải trước cho tất cả các tuyến đường ngay cả khi không bắt buộc. Do đó, bạn không nên thêm gợi ý preload theo cách thủ công.

    Tự động thêm trong quá trình hiển thị: Việc sử dụng khung để thêm gợi ý tải trước vào đầu tài liệu trong quá trình hiển thị trong ứng dụng CSR sẽ không giúp ích gì. Vì quá trình kết xuất xảy ra sau khi JavaScript được tải xuống và thực thi, nên <head> sẽ được kết xuất quá muộn để có bất kỳ giá trị nào.

    Đối với phiên bản đầu tiên của lệnh, sự kết hợp giữa gợi ý preconnect và gợi ý fetchpriority sẽ giúp ưu tiên hình ảnh thay vì preload. Tuy nhiên, Aurora hiện đang làm việc với nhóm Angular CLI để bật tính năng chèn tự động gợi ý tài nguyên tại thời điểm tạo bản dựng. Hãy chú ý theo dõi!

  2. Tối ưu hoá kích thước và định dạng hình ảnh trên máy chủ

    Vì các ứng dụng Angular thường được kết xuất phía máy khách, nên hình ảnh trên hệ thống tệp không thể được nén tại thời điểm yêu cầu và được phân phát nguyên trạng. Vì lý do này, bạn nên sử dụng CDN hình ảnh để nén hình ảnh và chuyển đổi chúng thành các định dạng hiện đại như WebP hoặc AVIF theo yêu cầu.

    Mặc dù chỉ thị này không thực thi việc sử dụng CDN hình ảnh, nhưng bạn nên sử dụng các CDN này với chỉ thị và trình tải tích hợp sẵn để đảm bảo sử dụng đúng tuỳ chọn cấu hình.

Tác động

Bản minh hoạ sau đây cho thấy sự khác biệt mà lệnh hình ảnh Angular có thể tạo ra đối với hiệu suất hình ảnh. Công cụ này so sánh hai trang web:

Trang web 1: Sử dụng các phần tử <img> gốc có hình ảnh được phân phát thông qua CDN Imgix (với các tuỳ chọn cấu hình mặc định).

Trang web thứ hai: Sử dụng lệnh image cho tất cả hình ảnh. Tệp này cũng bao gồm các biện pháp tối ưu hoá được đề xuất trực tiếp theo cảnh báo hoặc lỗi do lệnh đưa ra.

So sánh băng hình: Trang web Một có thẻ hình ảnh gốc so với Trang web Hai có lệnh hình ảnh Angular.

Nhóm đã làm việc với các đối tác để xác thực tác động của lệnh hình ảnh đối với hiệu suất của các ứng dụng Angular thực tế dành cho doanh nghiệp.

Một trong những đối tác này là Land's End. Dự kiến trang web của họ sẽ là một trường hợp kiểm thử phù hợp cho kết quả mà các ứng dụng thực tế có thể thấy.

Họ đã tiến hành kiểm thử trong phòng thí nghiệm Lighthouse trên môi trường đảm bảo chất lượng trước và sau khi sử dụng lệnh ảnh. Trên máy tính, LCP trung bình của họ giảm từ 12 giây xuống 3 giây, cải thiện 75% về LCP. Trên thiết bị di động, LCP trung bình giảm từ 20,2 giây xuống 12 giây (cải thiện 40,6%).

Lộ trình trong tương lai

Đây chỉ là phần đầu tiên của thiết kế cho lệnh ảnh Angular. Chúng tôi dự định sẽ bổ sung nhiều tính năng khác cho các phiên bản trong tương lai, bao gồm:

  • Hỗ trợ tốt hơn cho hình ảnh thích ứng:

    NgOptimizedImage hiện hỗ trợ sử dụng srcset, nhưng bạn phải cung cấp thuộc tính srcsetsizes theo cách thủ công cho từng hình ảnh. Trong tương lai, lệnh này có thể tự động tạo các thuộc tính srcsetsizes.

  • Tự động chèn gợi ý tài nguyên

    Bạn có thể tích hợp với Angular CLI để tạo thẻ kết nối trước và tải trước cho các hình ảnh LCP quan trọng.

  • Hỗ trợ Angular SSR

    Phiên bản MVP được thiết kế có tính đến các quy tắc ràng buộc CSR của Angular, nhưng cũng cần khám phá các giải pháp tối ưu hoá hình ảnh cho Angular SSR (angular/universal).

  • Cải tiến trải nghiệm của nhà phát triển

    NgOptimizedImage yêu cầu bạn phải chỉ định thuộc tính widthheight cho mỗi hình ảnh. Tuy nhiên, việc chỉ định các thông số này cho từng hình ảnh có thể gây phiền toái cho một số nhà phát triển. Có thể cải thiện trải nghiệm của nhà phát triển trong lần lặp lại tiếp theo như sau:

    1. Hỗ trợ một chế độ bổ sung (tương tự như tuỳ chọn bố cục hình ảnh "fill" trong Next.js) không yêu cầu xác định rõ chiều rộng/chiều cao.
    2. Sử dụng tính năng tích hợp CLI để tự động đặt chiều rộng và chiều cao cho hình ảnh cục bộ bằng cách xác định kích thước thực tế của hình ảnh.

Kết luận

Nhà phát triển sẽ có thể sử dụng lệnh hình ảnh Angular theo từng giai đoạn, bắt đầu từ phiên bản xem trước dành cho nhà phát triển trong phiên bản 14.2.0. Hãy dùng thử NgOptimizedImage và để lại ý kiến phản hồi!

Xin cảm ơn đặc biệt Katie Hempenius và Alex Castle đã đóng góp cho bài viết này.