Optymalizacja obrazów zgodnie z dyrektywą Angular Image

Kara Erickson
Kara Erickson
Leena Sohoni
Leena Sohoni

W maju 2022 r. zespoły Aurora i Angular ogłosiły, że będą współpracować nad dyrektywą dotyczącą obrazów w Angular. Ta dyrektywa została niedawno udostępniona w ramach wersji w poziomie podglądu dla deweloperów w ramach Angulara w wersji 14.2. W tym poście opisujemy, jak nowa dyrektywa image, NgOptimizedImage, obsługuje optymalizację obrazów w Angular.

Tło

Zdjęcia są częstym i ważnym elementem wygody użytkowników internetu.99, 9% stron internetowych generuje żądania dotyczące co najmniej jednego zdjęcia. Obrazy są też największym czynnikiem wpływającym na wagę strony, stanowiąc średnio 982 kilobajty na stronę.

Ze względu na rosnącą liczbę i rozmiar obrazów mogą one pogarszać wydajność stron internetowych i wpływać na dane podstawowych wskaźników internetowych. W 2021 r.79,4% stron na komputery stanowiły strony, których elementem o największym czasie renderowania (LCP) był obraz. Dążenie do optymalizacji obrazów stało się więc dla wielu z nas stałym zajęciem.

Zespół Aurora wierzy w potencjał frameworków, które umożliwiają tworzenie wbudowanych rozwiązań dla typowych problemów programistów. Pierwszym krokiem w zakresie optymalizacji obrazów był komponent obrazu Next.js. Uznali, że ten komponent może posłużyć jako pole do testowania, czy ulepszenie optymalizacji obrazów pod kątem wygody programistów może przełożyć się na lepsze wyniki w przypadku większej liczby aplikacji korzystających z ram.

Pierwszy zestaw wyników uzyskany przez użytkownika Next.js Leboncoin był zachęcający. Po zastosowaniu next/image firma Leboncoin odnotowała znaczną poprawę LCP (z 2,4 s na 1,7 s). Kolejne przyjęcie next/image w społeczności przyczyniło się do zwiększenia liczby źródeł Next.js, które spełniają wartości progowe LCP. Wkrótce pojawiły się prośby o dodanie podobnych funkcji w innych ramach, m.in. w Angularze.

W tym celu zespół Aurora skonsultował się z zespołem Angular i Nuxt, aby stworzyć prototypy komponentów obrazów dla tych frameworków. W zeszłym roku został wydany komponent obrazu Nuxt. Wprowadziliśmy dyrektywę dotyczącą obrazów w Angular (NgOptimizedImage), aby domyślnie optymalizować obrazy w ramach tej platformy.

Możliwość

Angular to jedna z najpopularniejszych platform JavaScriptu używana obecnie przez programistów. Jest on używany przez ponad 50 tys. źródeł zindeksowanych przez HTTPArchive na urządzeniach mobilnych. W przypadku NPM odnotowuje blisko 3 mln pobierania tygodniowo.

LCP w przypadku witryn Angular w ciągu ostatniego roku.

Patrząc na wyniki podstawowych wskaźników internetowych, odsetek miejsc pochodzenia w Angular, które spełniają progi LCP „dobra jakość”, wymaga jeszcze poprawy. W czerwcu 2022 r.tylko 18,74% witryn Angular miało na urządzeniach mobilnych dobry wynik LCP. Ponieważ obrazy stanowią element LCP w ponad 70% stron internetowych na urządzeniach mobilnych i komputerach, nieoptymalizowane obrazy LCP mogą być jedną z głównych przyczyn niższych wartości LCP w witrynach Angular.

Dyrektywa dotycząca obrazów w Angular została zaprojektowana tak, aby poprawić te liczby.

MVP w przypadku dyrektywy NgOptimizedImage

Wersja minimalna dyrektywy obrazu Angular opiera się na doświadczeniach z dotychczasowych komponentów obrazów Aurora, a jednocześnie dostosowuje projekt do renderowania po stronie klienta w Angular. Wiele standardowych problemów z optymalizacją obrazów zostało rozwiązanych przez:

  • Domyślne ustawienia o wysokiej jakości.
  • Wyrzucanie błędów lub ostrzeżeń w celu zapewnienia zgodności ze sprawdzonymi metodami.

Najważniejsze informacje o projektowaniu:

  1. Inteligentne leniwy ładowanie

    Obrazy, które są niewidoczne dla użytkownika podczas wczytywania strony (np. obrazy poniżej widocznej części strony lub ukryte obrazy w karuzeli), powinny być wczytywane opóźnione. Łatwo wczytywanie zwalnia zasoby przeglądarki, aby wczytać inne ważne teksty, multimedia lub skrypty. Większość obrazów nie jest krytycznych i powinna być wczytywana z opóźnieniem, ale w 2021 r.tylko 7,8% stron używało natywnej wczytywania z opóźnieniem.

    Dyrektywa image w Angularze wczytuje w ramach lazy load niekrytyczne obrazy, a tylko wczytuje obrazy specjalnie oznaczone jako priority. Dzięki temu większość obrazów będzie wczytywana optymalnie.

  2. Ustalanie priorytetów dla kluczowych obrazów

    Dodawanie wskazówek dotyczących zasobów (np. preload lub preconnect), aby nadać priorytety wczytywaniu najważniejszych obrazów, to sprawdzona metoda. Jednak większość aplikacji ich nie używa. Według Almanachu internetowego na rok 2021 tylko 12,7% stron mobilnych korzysta z wskazówek dotyczących wstępnego połączenia, a tylko 22,1% stron mobilnych korzysta z wskazówek dotyczących wstępnego wczytania.

    Gdy obrazy są oznaczone jako priorytetowe, dyrektywa dotycząca obrazów działa na 2 frontach.

    • Ustawia ona parametr fetchpriority obrazu na "high", aby przeglądarka wiedziała, że powinna pobrać obraz z wysokim priorytetem.
    • W trybie programowania sprawdzanie w czasie wykonywania potwierdza, że uwzględniono podpowiedź zasobu preconnect odpowiadającą źródłu obrazu.

    W trybie programistycznym dyrektywa korzysta też z interfejsu PerformanceObserver API, aby sprawdzić, czy obraz LCP został oznaczony jako priority zgodnie z oczekiwaniami. Jeśli nie jest on oznaczony jako priority, pojawia się błąd z instrukcją dodania atrybutu priority do obrazu LCP.

    Dzięki temu połączeniu automatyzacji i zgodności obraz LCP ma podpowiedź preconnect, wartość atrybutu fetchpriority high i nie jest ładowany z opóźnieniem.

  3. Konfiguracja zoptymalizowana pod kątem popularnych narzędzi do tworzenia obrazów

    Zalecamy, aby aplikacje Angular korzystały z CDN-ów obrazów, które często domyślnie zapewniają usługi optymalizacji.

    Dyrektywa zachęca do korzystania z CDN obrazów, zapewniając deweloperom szczególnie atrakcyjne środowisko konfigurowania tych sieci w aplikacji. Obsługuje interfejs ładowarki, który umożliwia zdefiniowanie dostawcy CDN i adresu URL podstawowego w konfiguracji. Po skonfigurowaniu wystarczy zdefiniować nazwę komponentu w znaczniku. Na przykład

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

    Jest to równoznaczne z uwzględnieniem tych tagów obrazu i zmniejsza ilość znaczników, które deweloperzy muszą uwzględnić w przypadku każdego obrazu.

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

    Dyrektywa image udostępnia wbudowane ładowarki z optymalną konfiguracją dla najpopularniejszych CDN-ów obrazów. Te ładowarki automatycznie formatują adresy URL obrazów, aby zapewnić używanie zalecanych ustawień formatu i kompresji obrazu w przypadku każdej sieci CDN.

  4. Wbudowane błędy i ostrzeżenia

    Oprócz powyższych wbudowanych optymalizacji dyrektywa zawiera też wbudowane mechanizmy kontroli, które sprawdzają, czy deweloperzy stosowali zalecane sprawdzone metody w znacznikach obrazów. Dyrektywa image wykonuje te kontrole.

    1. Obrazy bez rozmiaru: dyrektywa image powoduje błąd, jeśli znacznik obrazów nie ma zdefiniowanej wyraźnej szerokości i wysokości. Obrazy bez rozmiaru mogą powodować przesunięcia układu, które wpływają na dane o skumulowanym przesunięciu układu (CLS) strony. Aby tego uniknąć, zalecamy, aby obrazy miały określone atrybuty width i height.

    2. Format obrazu: dyrektywa image powoduje wyświetlenie błędu, aby poinformować programistów, że współczynnik proporcji width:height zdefiniowany w kodzie HTML nie jest zbliżony do rzeczywistego współczynnika proporcji wyrenderowanego obrazu. Może to spowodować zniekształcenie obrazu na ekranie. Może się tak zdarzyć, jeśli:

      1. przez pomył zdefiniowano nieprawidłowe wymiary (szerokość lub wysokość);
      2. Jeśli w kodzie CSS zdefiniujesz jeden wymiar za pomocą wartości procentowej, ale nie drugiego (np. width: 100% potrzebuje height: auto, aby obraz powiększał się w obu wymiarach).
    3. Obrazy o zbyt dużym rozmiarze: jeśli obraz nie definiuje atrybutu srcset, a obraz wewnętrzny jest znacznie większy niż renderowany obraz, dyrektywa wyświetli ostrzeżenie sugerujące użycie atrybutów srcsetsizes.

    4. Gęstość obrazu: jeśli spróbujesz umieścić w elementach srcset obraz o gęstości pikseli większej niż 3x, wystąpi błąd. Nie zalecamy używania znaczników o większej wartości niż 2x, ponieważ może to spowodować nieoczekiwane wczytywanie przez urządzenia mobilne o wysokiej rozdzielczości ogromnych obrazów. Co więcej, ludzkie oko nie widzi zbyt dużej różnicy przy powiększeniu powyżej 2 x.

Wyzwania

Głównym wyzwaniem podczas projektowania NgOptimizedImage było dostosowanie strategii optymalizacji obrazów do działania w ramach frameworku po stronie klienta. Domyślne renderowanie w Next.js to renderowanie po stronie serwera (SSR) lub generowanie statycznej witryny (SSG), a w Angular – renderowanie po stronie klienta (CSR). Chociaż Angular obsługuje bibliotekę SSR (angular/universal), większość aplikacji Angular (około 60%) korzysta z renderowania po stronie klienta.

Dyrektywa image jest w pełni przeznaczona do korzystania z serwera aplikacji klienckiej, aby dopasować ją do typowego zastosowania w aplikacjach Angular. Stworzyło to dodatkowe ograniczenia, więc zespół musiał przemyśleć sposób tworzenia konkretnych optymalizacji aplikacji CSR.

Oto niektóre z nich:

  1. Wskazówki dotyczące zasobów pomocniczych

    Wstępne wczytywanie najważniejszych zasobów pomaga przeglądarce wcześniej je wykryć. Uwzględnianie wskazówek dotyczących zasobów w aplikacjach Angular jest jednak skomplikowane, ponieważ:

    Ręczne dodawanie: dodanie preload ręcznie jest trudne dla programistów. Angular używa jednego udostępnionego pliku index.html dla całego projektu lub wszystkich ścieżek w witrynie. W związku z tym <head> dokumentu jest taki sam na każdej trasie (przynajmniej w momencie wyświetlania). Dodanie dowolnego podpowiedzi preload do <head> oznaczałoby, że zasób zostanie wstępnie wczytany na wszystkich trasach, nawet tam, gdzie nie jest to wymagane. Dlatego nie zalecamy ręcznego dodawania podpowiedzi preload.

    Automatyczne dodawanie podczas renderowania: korzystanie z ramy do dodawania wskazówek dotyczących wstępnego ładowania do nagłówka dokumentu podczas renderowania w aplikacji CSR nie pomaga. Renderowanie następuje po pobraniu i wykonaniu kodu JavaScriptu, więc wartość <head> zostanie wyrenderowana zbyt późno, aby mogła być użyta.

    W przypadku pierwszej wersji dyrektywy kombinacja podpowiedzi preconnectfetchpriority służy do nadawania priorytetu obrazowi zamiast preload. Zespół Aurora współpracuje obecnie z zespołem interfejsu wiersza poleceń Angular, aby umożliwić automatyczne wstrzykiwanie wskazówek dotyczących zasobów w czasie kompilacji.

  2. Optymalizowanie rozmiaru i formatu obrazu na serwerze

    Aplikacje Angular są zwykle renderowane po stronie klienta, więc obrazy w systemie plików nie mogą być kompresowane w momencie wysyłania żądania i są wyświetlane w postaci oryginalnej. Z tego powodu zalecamy korzystanie z CDN obrazów w celu kompresowania obrazów i konwersji ich na nowoczesne formaty, takie jak WebP lub AVIF, na żądanie.

    Chociaż dyrektywa nie nakazuje korzystania z CDN obrazów, zdecydowanie zalecamy ich używanie z dyrektywą. Wbudowane ładowarki zapewniają prawidłowe użycie opcji konfiguracji.

Wpływ

Demo poniżej pokazuje, jak dyrektywa obrazu w Angular może wpłynąć na wydajność obrazu. Porównuje 2 strony internetowe:

Witryna 1:korzysta z elementów natywnych <img> z obrazami wyświetlanymi przez sieć CDN Imgix (z domyślnymi opcjami konfiguracji).

Druga witryna: użyj dyrektywy image dla wszystkich obrazów. Zawiera on też optymalizacje zalecane bezpośrednio przez ostrzeżenia lub błędy wygenerowane przez dyrektywę.

Porównanie paska filmowego: witryna 1 z natywnymi tagami obrazów a witryna 2 z direktiwą obrazów Angular

Współpracując z partnerami, zespół potwierdził wpływ dyrektywy obrazów na wydajność rzeczywistych aplikacji korporacyjnych Angular.

Jednym z tych partnerów była Land's End. Oczekiwaliśmy, że ich witryna będzie dobrym przykładem testowym wyników, które mogą uzyskać prawdziwe aplikacje.

Testy Lighthouse zostały przeprowadzone w środowisku QA przed i po użyciu dyrektywy obrazów. Na komputerach mediana LCP spadła z 12,0 s do 3,0 s, co oznacza poprawę o 75%. Na urządzeniach mobilnych mediana LCP spadła z 20,2 s na 12,0 s (poprawa o 40,6%).

Przyszłe plany

To tylko pierwsza część projektu dyrektywy obrazu w Angularze. W przyszłych wersjach planujemy wprowadzić wiele innych funkcji, m.in.:

  • Lepsza obsługa elastycznych obrazów:

    NgOptimizedImage obsługuje obecnie atrybuty srcset, ale atrybuty srcset i sizes należy podać ręcznie w przypadku każdego obrazu. W przyszłości dyrektywa może automatycznie generować atrybuty srcsetsizes.

  • Automatyczne wstrzykiwanie wskazówek dotyczących zasobów

    Możesz zintegrować się z interfejsem wiersza poleceń Angular, aby generować tagi wstępnego połączenia i wstępnego wczytania dla kluczowych obrazów LCP.

  • Obsługa Angular SSR

    Wersja MVP została zaprojektowana z uwzględnieniem ograniczeń Angular CSR, ale warto też przyjrzeć się rozwiązaniom optymalizacji obrazów dla Angular SSR (angular/universal).

  • Ulepszenia dotyczące interfejsu dla deweloperów

    W przypadku atrybutu NgOptimizedImage należy podać atrybuty width i height dla każdego obrazu. Jednak ich określanie w przypadku każdego obrazu może być uciążliwe dla niektórych deweloperów. W kolejnych wersjach możemy wprowadzić ulepszenia dla deweloperów:

    1. Obsługa dodatkowego trybu (podobnego do opcji układu obrazu „fillw Next.js), który nie wymaga definiowania dokładnej szerokości ani wysokości.
    2. Korzystanie z integracji z interfejsem wiersza poleceń, aby automatycznie ustawiać szerokość i wysokość zdjęć lokalnych przez określenie rzeczywistych wymiarów obrazu.

Podsumowanie

Dyrektywa obrazów w Angular będzie udostępniana deweloperom etapami, zaczynając od wersji w wersji wstępnej dla deweloperów 14.2.0. Wypróbuj NgOptimizedImage i prześlij opinię.

Szczególne podziękowania dla Katie Hempenius i Alexandra Castle’a za ich wkład.