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 để triển khai chỉ thị về hình ảnh cho Angular. Chỉ thị này gần đây đã được phát hành cho bản dùng thử cho nhà phát triển dưới dạng một phần của Angular phiên bản 14.2. Bài đăng này nói về cách lệnh hình ảnh mới (NgOptimizedImage) hỗ trợ hoạt động 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 web, với 99,9% số trang web tạo yêu cầu liên quan đến một hoặc nhiều hình ảnh. Hình ảnh cũng là yếu tố quan trọng nhất ảnh hưởng đến trọng lượng trang, chiếm trung bình là 982 kilobyte trên 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ố trong Chỉ số quan trọng chính của trang web. Đối với 79,4% số trang dành cho máy tính, hình ảnh là phần tử Nội dung lớn nhất hiển thị (LCP) vào năm 2021. Do đó, việc theo đuổi hình ảnh tối ưu hoá đã trở thành nỗ lực không ngừng của nhiều người trong chúng ta.

Đội ngũ Aurora tin tưởng vào việc tận dụng sức mạnh của các khung làm việc để cung cấp các giải pháp tích hợp cho những thách thức thường gặp của nhà phát triển. Bước đột phá đầu tiên của họ vào không gian 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à nơi thử nghiệm xem việc cải thiện trải nghiệm tối ưu hoá hình ảnh (DX) của nhà phát triển 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.

Nhóm 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ể LCP (từ 2,4 giây lên 1,7 giây) sau khi họ bắt đầu sử dụng next/image. Việc sử dụng next/image tiếp theo trong cộng đồng đã góp phần làm tăng số nguồn gốc Next.js đáp ứng ngưỡng LCP. Chẳng bao lâu nữa, đã có yêu cầu cho các tính năng tương tự trong các khung khác, một trong số đó là Góc.

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

Cơ hội

Angular là một trong những khung JavaScript hàng đầu được các nhà phát triển sử dụng hiện nay. API này được hơn 50 nghìn nguồn gốc thu thập thông tin do HTTPArchive thu thập 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 kháng nghị

LCP cho các trang web Angular trong một năm qua.

Nhìn vào điểm số Core Web Vitals, tỷ lệ phần trăm 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% số 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 trang web Angular.

Lệnh hình ảnh Angular được thiết kế để giúp cải thiện những số liệu này.

MVP cho lệnh NgOptimizedImage

MVP của lệnh hình ảnh Angular được xây dựng dựa trên những bài học từ các thành phần hình ảnh mà Aurora đã xây dựng cho đến nay trong khi đ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 đề khi tối ưu hoá hình ảnh chuẩn đã được giải quyết bằng một trong hai cách sau:

  • Cung cấp 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ế này:

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

    Hình ảnh không hiển thị với người dùng khi tải trang (ví dụ: hình ảnh dưới màn hình đầu tiên hoặc hình ảnh băng chuyền ẩn) tốt nhất nên được tải từng phần. Tính năng tải từng phần giúp giải phóng các tài nguyên của 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 đều không quan trọng và nên được tải từng phần, nhưng chỉ có 7,8% số trang sử dụng tính năng tải từng phần gốc vào năm 2021.

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

  2. Sắp xếp thứ tự ưu tiên các hình ảnh quan trọng

    Thêm gợi ý về 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 dùng đến phần mở rộng này. Theo Niên giám web năm 2021, chỉ 12,7% trang dành cho thiết bị di động sử dụng gợi ý kết nối trước và chỉ 22,1% trang dành cho thiết bị di động sử dụng gợi ý tải trước.

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

    • Thao tác này sẽ đặt tìm nạp ưu tiên hình ảnh thành "high" để trình duyệt biết rằng nên 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 sẽ 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 dùng API PerformanceObserver để xác minh rằng hình ảnh LCP đã được đánh dấu priority là dự kiến. Nếu thuộc tính này không được đánh dấu là priority, sẽ xảy ra 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ự độ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 tải từng phần.

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

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

    Chỉ thị này khuyến khích nhà phát triển sử dụng CDN hình ảnh bằng cách mang đến trải nghiệm đặc biệt hấp dẫn cho nhà phát triển (DX) để định cấu hình các CDN trong ứng dụng. Lệnh này hỗ trợ một 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 của mình. Sau khi định cấu hình, bạn chỉ phải xác định tên tài sả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 đây và giảm bớt công sức mà nhà phát triển thẻ đánh dấu phải đưa vào cho mỗi hình ảnh.

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

    Lệnh hình ảnh cung cấp các 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. Những trình tải này sẽ tự động định dạng URL hình ảnh để đảm bảo sử dụng các chế độ cài đặt nén và định dạng hình ảnh đề xuất cho mỗi 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 ở trên, lệnh này cũng có các bước kiểm tra tích hợp để đảm bảo rằng nhà phát triển đã tuân theo các phương pháp hay nhất được đề xuất về mã đánh dấu hình ảnh. Lệnh hình ảnh thực hiện các bước kiểm tra sau đây.

    1. Hình ảnh chưa có kích thước: Lệnh hình ảnh sẽ báo lỗi nếu mã đánh dấu hình ảnh không xác định chiều rộng và chiều cao rõ ràng. Hình ảnh chưa định kích thước có thể gây thay đổi bố cục, ảnh hưởng đến chỉ số Điểm thay đổi bố cục (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 chỉ định các thuộc tính widthheight.

    2. Tỷ lệ khung hình: Lệnh hình ảnh sẽ báo lỗi để cho nhà phát triển biết liệu tỷ lệ khung hình width:height được xác định trong HTML có không gần với tỷ lệ khung hình thực tế của hình ảnh được kết xuất hay không. Đ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 phát triển ở cả hai chiều).
    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 kết xuất, thì lệnh sẽ hiển thị cảnh báo đề xuất việc sử dụng các thuộc tính srcsetsizes.

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

Thử thách

Việc điều chỉnh các chiến lược tối ưu hoá hình ảnh cho phù hợp với hoạt động trong khung phía máy khách là một thách thức chính khi thiết kế NgOptimizedImage. Trải nghiệm hiển thị 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), trong khi 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 – góc/phổ thông – nhưng hầu hết ứng dụng Angular (~60%) đều sử dụng CSR.

Lệnh hình ảnh được xây dựng 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 các hạn chế bổ sung và đội ngũ của chúng tôi phải suy nghĩ lại về cách xây dựng các biện pháp tối ưu hoá cụ thể cho các ứng dụng CSR.

Bạn gặp phải một số thách thức như sau:

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

    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 các gợi ý tài nguyên vào ứng dụng Angular khá phức tạp vì:

    Thêm 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ệpindex.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 là giống nhau ở mọi tuyến (ít nhất là tại thời gian cung cấp). 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, 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 kết xuất: Việc sử dụng khung để thêm gợi ý tải trước vào phần đầu tài liệu trong quá trình kết xuất trong ứng dụng CSR không có tác dụng. Vì hiển thị xảy ra sau khi JavaScript được tải xuống và thực thi, nên <head> sẽ 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 này, tổ hợp gợi ý preconnect và gợi ý fetchpriority sẽ phân phát để ư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 tự động chèn các gợi ý về tài nguyên trong thời gian xây dựng. Hãy chú ý theo dõi nhé!

  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 hiển thị phía máy khách, nên hình ảnh trên hệ thống tệp không thể nén được 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ù lệnh này không thực thi việc sử dụng CDN của hình ảnh, nhưng bạn nên sử dụng cùng với lệnh và các trình tải tích hợp sẵn giúp đảm bảo dùng đúng tuỳ chọn cấu hình.

Mức độ tác động

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

Trang web thứ nhất: Sử dụng các phần tử <img> gốc có hình ảnh phân phát qua CDN của Imgix (với các lựa chọn cấu hình mặc định).

Trang web 2: Sử dụng lệnh hình ảnh cho tất cả các hình ảnh. Ngoài ra, chỉ số này còn có các hoạt động tối ưu hoá được đề xuất trực tiếp khi có cảnh báo hoặc lỗi do lệnh tạo ra.

So sánh cuộn phim: Trang web 1 có thẻ hình ảnh gốc so với Trang web 2 có lệnh hình ảnh Angular.

Nhóm này đã 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 thử nghiệm tốt cho những kết quả mà các ứng dụng thực tế có thể thấy.

Hoạt động kiểm thử Lighthouse trong phòng thí nghiệm đã được thực hiện trên môi trường đảm bảo chất lượng của họ trước và sau khi sử dụng lệnh hình ảnh. Trên máy tính, LCP trung bình của họ giảm từ 12 giây xuống còn 3 giây, tức là cải thiện 75% 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 tương lai

Đây chỉ là phần đầu tiên của thiết kế cho lệnh hình ảnh Angular. Chúng tôi dự định cung cấp 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 các thuộc tính srcsetsizes cho từng hình ảnh theo cách thủ công. 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 kết nối trước và tải trước thẻ cho các hình ảnh LCP quan trọng.

  • Hỗ trợ Angular SSR

    Phiên bản MVP được thiết kế dựa trên các quy tắc ràng buộc của Angular CSR, nhưng bạn cũng cần tìm hiểu các giải pháp tối ưu hoá hình ảnh cho Angular SSR (góc/phổ thông).

  • Các điểm cải tiến về trải nghiệm cho nhà phát triển

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

    1. Hỗ trợ 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 chiều rộng/chiều cao rõ ràng.
    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

Chỉ thị hình ảnh Angular sẽ được cung cấp cho nhà phát triển 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 ở phiên bản 14.2.0. Hãy dùng thử NgOptimizedImage và để lại ý kiến phản hồi!

Xin đặc biệt cảm ơn Katie Hempenius và Alex Castle vì những đóng góp của họ.