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.
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:
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.Ustalanie priorytetów dla kluczowych obrazów
Dodawanie wskazówek dotyczących zasobów (np.
preload
lubpreconnect
), 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 jakopriority
, pojawia się błąd z instrukcją dodania atrybutupriority
do obrazu LCP.Dzięki temu połączeniu automatyzacji i zgodności obraz LCP ma podpowiedź
preconnect
, wartość atrybutufetchpriority
high
i nie jest ładowany z opóźnieniem.- Ustawia ona parametr fetchpriority obrazu na
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.
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.
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
iheight
.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:- przez pomył zdefiniowano nieprawidłowe wymiary (szerokość lub wysokość);
- Jeśli w kodzie CSS zdefiniujesz jeden wymiar za pomocą wartości procentowej, ale nie drugiego (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 wewnętrzny jest znacznie większy niż renderowany obraz, dyrektywa wyświetli ostrzeżenie sugerujące użycie atrybutówsrcset
isizes
.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:
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 podpowiedzipreload
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 podpowiedzipreload
.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
preconnect
ifetchpriority
służy do nadawania priorytetu obrazowi zamiastpreload
. 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 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ę.
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 atrybutysrcset
, ale atrybutysrcset
isizes
należy podać ręcznie w przypadku każdego obrazu. W przyszłości dyrektywa może automatycznie generować atrybutysrcset
isizes
.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ć atrybutywidth
iheight
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. - Korzystanie z integracji z interfejsem wiersza poleceń, aby automatycznie ustawiać szerokość i wysokość 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 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.