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 kluczowym 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 wyrenderowania (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ło użycie komponentu obrazu Next.js. Uznali, że ten komponent może posłużyć jako pole do testów, które pozwoli sprawdzić, czy ulepszenie optymalizacji obrazów z perspektywy dewelopera może przynieść 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 włączeniu 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 Angular.
W związku z tym Aurora skonsultowała 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ę obrazów w Angular (NgOptimizedImage), aby domyślnie optymalizować obrazy w ramach Angular.
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 i ma prawie 3 miliony pobrań tygodniowo w NPM.
Patrząc na wyniki podstawowych wskaźników internetowych, odsetek źródeł Angular, które spełniają progi „dobra” LCP, nadal wymaga 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 w Angular opiera się na doświadczeniach z dotychczasowych komponentów obrazów w Aurora, a także na dostosowaniu projektu 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:
- Inteligentne leniwe ł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 Angularu leniwie ładuje niekrytyczne obrazy, a tylko chętnie ładuje obrazy specjalnie oznaczone jako - priority. Dzięki temu większość obrazów będzie wczytywana optymalnie.
- Ustalanie priorytetów dla kluczowych obrazów - Dodawanie wskazówek dotyczących zasobów (np. - preloadlub- preconnect), aby nadać priorytety wczytywaniu najważniejszych obrazów, to sprawdzona metoda. Większość aplikacji ich jednak 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 on 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 preconnectodpowiadającą źródłu obrazu.
 - W trybie programowania dyrektywa korzysta też z interfejsu PerformanceObserver API, aby sprawdzić, czy obraz LCP został oznaczony jako - priorityzgodnie z oczekiwaniami. Jeśli nie jest on oznaczony jako- priority, pojawia się błąd z instrukcją dodania atrybutu- prioritydo obrazu LCP.- Dzięki temu połączeniu automatyzacji i zgodności obraz LCP ma podpowiedź - preconnect, wartość atrybutu- fetchpriority- highi nie jest ładowany z opóźnieniem.
- Ustawia on fetchpriority obrazu na 
- Optymalna konfiguracja dla popularnych narzędzi do edycji zdjęć - 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 możliwości konfigurowania ich 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 znacznikach. 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. 
- Wbudowane błędy i ostrzeżenia - Oprócz powyższych wbudowanych optymalizacji dyrektywa zawiera też wbudowane mechanizmy kontroli, które sprawdzają, czy deweloperzy stosują zalecane sprawdzone metody w znacznikach obrazów. Dyrektywa image wykonuje te kontrole. - 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 - widthi- height.
- Format obrazu: dyrektywa image powoduje wyświetlenie błędu, aby poinformować programistów, że współczynnik proporcji - width:- heightzdefiniowany w 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:- przez pomył zdefiniowano nieprawidłowe wymiary (szerokość lub wysokość);
- Jeśli w kodzie CSS zdefiniujesz jeden wymiar jako procent, ale nie drugi (np. width: 100%potrzebujeheight: auto, aby obraz powiększał się w obu wymiarach).
 
- Obrazy o zbyt dużym rozmiarze: jeśli obraz nie definiuje atrybutu - srcset, a obraz własny jest znacznie większy niż renderowany obraz, dyrektywa wyświetli ostrzeżenie sugerujące użycie atrybutów- srcseti- sizes.
- Gęstość obrazu: jeśli spróbujesz umieścić w elementach - srcsetobraz o gęstości pikseli większej niż- 3x, wystąpi błąd. Nie zalecamy używania większych wartości niż- 2x, ponieważ może to spowodować nieoczekiwane wczytywanie przez urządzenia mobilne dużych obrazów w wysokiej rozdzielczości. 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 tworzenia komponentów po stronie serwera, aby pasowała 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:
- 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: deweloperzy mają trudności z ręcznym dodawaniem sugestii dotyczącej zasobu - preload. 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- preloaddo- <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 - preconnecti- fetchprioritysł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.
- 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, aby kompresować obrazy i konwertować je na nowoczesne formaty takie jak WebP lub AVIF na żądanie. - Chociaż dyrektywa nie nakazuje korzystania z CDN obrazów, zdecydowanie zalecamy ich używanie w połączeniu z dyrektywą. Wbudowane ładowarki zapewniają prawidłowe użycie opcji konfiguracji. 
Wpływ
Poniższy pokaz slajdów pokazuje, jak dyrektywa obrazu w Angular może wpłynąć na wydajność obrazu. Porównuje 2 witryny:
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ę.
Współpraca z partnerami pozwoliła zespołowi zweryfikować wpływ dyrektywy obrazów na wydajność rzeczywistych aplikacji korporacyjnych opartych na 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 dodać wiele innych funkcji, w tym:
- Lepsza obsługa obrazów elastycznie dostosowywanych: - NgOptimizedImageobsługuje obecnie atrybuty- srcset, ale atrybuty- srcseti- sizesnależy podać ręcznie w przypadku każdego obrazu. W przyszłości dyrektywa może automatycznie generować atrybuty- srcseti- sizes.
- Automatyczne wstrzykiwanie wskazówek dotyczących zasobów - Możesz zintegrować się z interfejsem wiersza poleceń Angular, aby wygenerować 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 ważne jest też zbadanie rozwiązań optymalizacji obrazów dla Angular SSR (angular/universal). 
- Ulepszenia dotyczące interfejsu dla deweloperów - W przypadku atrybutu - NgOptimizedImagewymagane jest, aby atrybuty- widthi- heightbyły określone 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:- Obsługa dodatkowego trybu (podobnego do opcji układu obrazu „fill” w Next.js), który nie wymaga definiowania dokładnej szerokości ani wysokości.
- Użycie integracji z CLI do automatycznego ustawiania szerokości i wysokości zdjęć lokalnych przez określenie rzeczywistych wymiarów obrazu.
 
- Obsługa dodatkowego trybu (podobnego do opcji układu obrazu „
Podsumowanie
Dyrektywa obrazów w Angular będzie udostępniana deweloperom etapami, zaczynając od wersji w wersji 14.2.0 przeznaczonej dla programistów. Wypróbuj NgOptimizedImage i prześlij opinię.
Szczególne podziękowania dla Katie Hempenius i Alexandra Castle’a za ich wkład.