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ẽ hợp tác để đưa ra một chỉ thị về hình ảnh cho Angular. Gần đây, lệnh này đã được phát hành trong bản dùng thử cho nhà phát triển như một phần của Angular v14.2. Bài đăng này nói về cách lệnh hình ảnh mới (NgOptimizedImage) hỗ trợ việc 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 tạo yêu cầu về một hoặc nhiều hình ảnh. Hình ảnh cũng là những yếu tố quan trọng nhất tạo nên trọng lượng 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ố trong Các chỉ số quan trọng về trang web. Đối với 79,4% số trang trên máy tính, hình ảnh là phần tử 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 trong chúng ta.

Đội ngũ Aurora tin tưởng vào việc khai thác 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 sẵn cho những thách thức phổ biến của nhà phát triển. Họ xâm nhập đầu tiên 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 để xác định xem việc cải thiện trải nghiệm của nhà phát triển (DX) về việc tối ưu hoá hình ảnh có thể mang lại hiệu suất tốt hơn cho nhiều ứng dụng hơn sử dụng khung hay không.

Nhóm kết quả đầu tiên từ người dùng Next.js Leboncoin rất khả quan. Leboncoin nhận thấy LCP cải thiện đáng kể (từ 2,4 giây lên 1,7 giây) sau khi bắt đầu sử dụng next/image. Việc sử dụng next/image trong cộng đồng tiếp theo đóng vai trò trong việc gia tăng các 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à Angular.

Do đó, Aurora đã tham khảo ý kiến của Angular và Nuxt để thiết kế các thành phần hình ảnh nguyên mẫu 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 để mang các chế độ mặc định tối ưu hoá hình ả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. Thư viện này được hơn 50 nghìn nguồn gốc thu thập dữ liệu bởi HTTPArchive trên thiết bị di động và tự hào có gần 3 triệu lượt tải xuống mỗi tuần trên ALIAS.

LCP (Thời gian hiển thị nội dung lớn nhất) cho các trang web Angular trong 1 năm qua.

Theo điểm số Các chỉ số quan trọng về trang web, 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. Vào tháng 6 năm 2022, chỉ 18,74% trang web Angular có LCP tốt trên thiết bị di động. Vì hình ảnh là yếu tố LCP cho hơn 70% trang web trên thiết bị di động và máy tính, nên hình ảnh LCP không được tối ưu hoá có thể là một trong những nguyên nhân chính gây ra LCP kém hơn trên các trang web Angular.

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

MVP cho lệnh NgOptimizedImage

MVP của chỉ thị 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 chuẩn đã được giải quyết bằng một trong hai cách sau:

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

Dưới đây là những điểm nổi bật về 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 dưới màn hình đầu tiên hoặc hình ảnh băng chuyền ẩn) nên là hình ảnh tải từng phần. Tính năng Tải từng phần giúp giải phóng tài nguyên của trình duyệt để tải các 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ỉ 7,8% số trang sử dụng tính năng tải từng phần gốc trong năm 2021.

    Theo mặc định, lệnh hình ảnh Angular tải từng phần của các hình ảnh không quan trọng và chỉ tải 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 các hình ảnh thể hiện hành vi tải tối ưu.

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

    Thêm gợi ý về tài nguyên (ví dụ: preload hoặc preconnect) để ưu tiên việc tải các hình ảnh quan trọng là phương pháp hay nhất được đề xuất. Tuy nhiên, hầu hết các ứng dụng không dùng đến. Theo Niên giám web năm 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.

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

    • Thuộc tính này đặt mức độ ưu tiên tìm nạp của hình ảnh thành "high" để trình duyệt biết rằng trình duyệt nên tải hình ảnh xuống có 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 một gợi ý về 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 priority như dự kiến. Nếu bạn không đánh dấu thuộc tính này là priority thì hệ thống sẽ báo 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à tính 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ụ chụp ảnh phổ biến

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

    Lệnh này khuyến khích sử dụng CDN hình ảnh bằng cách cung cấp trải nghiệm (DX) đặc biệt hấp dẫn cho nhà phát triển để đị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ỉ 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 đây và giảm bớt mã đánh dấu mà nhà phát triển 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">
    

    Chỉ thị hình ảnh cung cấp các trình tải tích hợp có 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 định dạng hình ảnh được đề xuất và chế độ cài đặt nén được sử dụng cho từng CDN.

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

    Ngoài các tính năng tối ưu hoá tích hợp ở trên, lệnh này cũng có các quy trình kiểm tra tích hợp để đảm bảo rằng nhà phát triển đã làm theo các phương pháp hay nhất được đề xuất trong thẻ đánh dấu hình ảnh. Lệnh hình ảnh thực hiện các bước kiểm tra sau.

    1. Hình ảnh chưa xác định: Lệnh hình ảnh gửi lỗi nếu thẻ đá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 không có kích thước có thể gây ra sự thay đổi về bố cục, ảnh hưởng đến chỉ số Điểm số tổng hợp về mức thay đổi bố cục (CLS) của trang. Phương pháp hay nhất để ngăn chặn điều này là hình ảnh nên có các thuộc tính widthheight.

    2. Tỷ lệ khung hình: Lệnh hình ảnh gửi lỗi để cho nhà phát triển biết liệu tỷ lệ khung hình của 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 kết xuất hay không. Việc 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 phương diện theo tỷ lệ phần trăm trong CSS, nhưng chưa xác định phương diện 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 hàm nội tại lớn hơn đáng kể so với hình ảnh kết xuất, thì lệnh này sẽ hiển thị cảnh báo đề xuất bạn sử dụng các thuộc tính srcsetsizes.

    4. Mật độ hình ảnh: Lệnh này sẽ gửi một 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. Thường thì bạn không nên dùng các phần mô tả cao hơn 2x vì điều này có hậu quả ngoài ý muốn là buộc thiết bị di động có độ phân giải cao tải xuống những hình ảnh lớn. Hơn nữa, mắt người thực sự không thể phân biệt được nhiều trê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 để 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à Hiển thị phía máy chủ (SSR) hoặc Tạo trang web tĩnh (SSG), trong khi đó trên Angular là Hiển thị phía máy khách (CSR). Mặc dù Angular hỗ trợ thư viện SSR – angular/universal – hầu hết các ứ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 những ràng buộc bổ sung và đội ngũ 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.

Một số thử thách đã gặp phải như sau:

  1. Gợi ý về tài nguyên hỗ trợ

    Việc 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 trong ứng dụng Angular rất phức tạp vì:

    Thêm thủ công: Nhà phát triển khó 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 trên trang web. Do đó, <head> của tài liệu là giống nhau cho mọi tuyến (ít nhất là tại thời điểm 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 mọi 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 khi 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 khi kết xuất trong ứng dụng CSR không giúp ích được gì. Vì quá trình kết xuất diễn ra sau khi JavaScript được tải xuống và thực thi, nên <head> sẽ hiển thị quá muộn nên không thể có bất kỳ giá trị nào.

    Đối với phiên bản đầu tiên của lệnh, tổ hợp gợi ý preconnect và gợi ý fetchpriority sẽ đóng vai trò để ưu tiên hình ảnh thay cho preload. Tuy nhiên, Aurora hiện đang làm việc với nhóm CLI Angular để bật tính năng tự động chèn gợi ý tài nguyên tại thời điểm 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 các hình ảnh trên hệ thống tệp không được nén tại thời điểm yêu cầu mà sẽ được phân phát theo nguyên trạng. Vì lý do này, bạn nên sử dụng CDN hình ảnh để nén và chuyển đổi hình ảnh thành đị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 hình ảnh, nhưng bạn nên sử dụng cùng với lệnh này và các trình tải tích hợp để đảm bảo sử dụng đúng lựa 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. Google Tìm kiếm so sánh hai trang web:

Trang web 1: Sử dụng phần tử <img> gốc với hình ảnh được phân phát thông qua CDN 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ả hình ảnh. Phiên bản này cũng bao gồm các biện pháp tối ưu hoá do các cảnh báo hoặc lỗi do lệnh đó trực tiếp đề xuất.

So sánh cuộn phim: Trang web Một có thẻ hình ảnh gốc so với Trang web 2 có chỉ thị 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 về hiệu suất của chỉ thị về hình ảnh đối với các ứng dụng Angular dành cho doanh nghiệp thực tế.

Một trong những đối tác đó là Land's End. Họ kỳ vọng 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 có thể thấy.

Thử nghiệm trong phòng thí nghiệm Lighthouse đượ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 chỉ thị hình ảnh. Trên máy tính để bàn, LCP trung bình của họ giảm từ 12,0 giây xuống 3,0 giây, cải thiện LCP 75%. Trên thiết bị di động, LCP trung bình giảm từ 20,2 giây xuống 12,0 giây (cải thiện 40,6%).

Lộ trình cho tương lai

Đây chỉ là phần đầu tiên của thiết kế cho lệnh hình ảnh Angular. Nhiều tính năng khác được lên kế hoạch 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ợ việc sử dụng srcset, nhưng các thuộc tính srcsetsizes phải được cung cấp theo cách thủ công cho mỗi 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 ý về 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 các thẻ cho các hình ảnh LCP quan trọng.

  • Hỗ trợ SSR góc nhìn

    Phiên bản MVP được thiết kế có tính đến các hạn chế CSR của Angular, nhưng điều quan trọng là cần khám phá các giải pháp tối ưu hoá hình ảnh cho Angular SSR (góc/phổ thông).

  • Cải thiện trải nghiệm dành cho nhà phát triển

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

    1. Hỗ trợ một chế độ bổ sung (tương tự như "fill" tuỳ chọn bố cục hình ảnh trong Next.js) mà không yêu cầu 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 các 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!

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