Łatwe wykonywanie zadań w internecie – Google I/O 2018

Na konferencji Google IO 2018 przedstawiliśmy przegląd narzędzi, bibliotek i technik optymalizacji, które ułatwiają poprawę wydajności witryn internetowych. Tutaj wyjaśniamy je na przykładzie aplikacji The Oodles Theater. Opowiadamy też o naszych eksperymentach z wczytywaniem predykcyjnym i nowej inicjatywie Guess.js.

Addy Osmani
Addy Osmani
Ewa Gasperowicz

W ciągu ostatniego roku sporo czasu poświęciliśmy na szukanie sposobów na przyspieszenie i zwiększenie wydajności internetu. W efekcie powstały nowe narzędzia, metody i biblioteki, o których chcemy Ci opowiedzieć w tym artykule. W pierwszej części pokażemy Ci kilka technik optymalizacji, których używaliśmy w praktyce podczas tworzenia aplikacji Oodles Theater. W drugiej części opowiemy o naszych eksperymentach z wczytywaniem predykcyjnym i nowej inicjatywie Guess.js.

Potrzebna wydajność

Internet staje się coraz cięższy. Jeśli sprawdzimy stan internetu, zobaczymy, że średnia strona mobilna waży około 1,5 MB, z czego większość to kod JavaScript i obrazy.

Rosnący rozmiar stron internetowych i inne czynniki, takie jak czas oczekiwania w sieci, ograniczenia procesora, wzorce blokowania renderowania czy nadmiarowy kod spoza witryny, sprawiają, że ta skomplikowana łamigłówka jest trudna.

Większość użytkowników twierdzi, że szybkość jest na samym szczycie hierarchii potrzeb związanych z UX. Nie jest to zbyt zaskakujące, ponieważ tak naprawdę nie można wiele zrobić, dopóki strona nie zostanie wczytana. Nie możesz wyciągać wartości z tej strony ani podziwiać jej estetyki.

Hierarchia UX w postaci piramidy
Rys. 1. Jak ważna jest szybkość dla użytkowników? (Szybkość ma znaczenie, t. 3)

Wiemy, że skuteczność ma znaczenie dla użytkowników, ale czasami trudno jest znaleźć właściwe miejsce do optymalizacji. Na szczęście istnieją narzędzia, które mogą Ci w tym pomóc.

Lighthouse – podstawa procesu związanego z wydajnością

Lighthouse to część Narzędzi deweloperskich w Chrome, która umożliwia przeprowadzenie audytu witryny i podpowiada, jak ją ulepszyć.

Niedawno wprowadziliśmy wiele nowych audytów wydajności, które są bardzo przydatne w codziennej pracy programisty.

Nowe audyty Lighthouse
Rys. 2. Nowe audyty Lighthouse

Zobaczmy, jak można z nich korzystać na przykładzie aplikacji Oodles Theater. To mała demonstracyjna aplikacja internetowa, w której możesz wypróbować niektóre z naszych ulubionych interaktywnych Doodle Google, a nawet zagrać w kilka gier.

Podczas tworzenia aplikacji zależało nam na tym, aby była ona jak najbardziej wydajna. Punktem początkowym optymalizacji był raport Lighthouse.

Raport Lighthouse dotyczący aplikacji Oodles
Rys. 3. Raport Lighthouse dotyczący aplikacji Oodles

Początkowa wydajność naszej aplikacji, jaką odnotowaliśmy w raporcie Lighthouse, była dość słaba. W sieci 3G użytkownik musiał czekać 15 sekund na pierwszą znaczącą aktualizację lub na to, aż aplikacja stanie się interaktywna. Lighthouse wskazał mnóstwo problemów z naszą witryną, a ogólna ocena skuteczności 23 to właśnie odzwierciedlenie tych problemów.

Strona ważyła około 3,4 MB – musieliśmy więc pozbyć się zbędnych elementów.

To było nasze pierwsze wyzwanie związane z wydajnością: znalezienie elementów, które można łatwo usunąć bez wpływu na ogólne wrażenia.

Możliwości optymalizacji skuteczności

Usuwanie niepotrzebnych zasobów

Są pewne oczywiste elementy, które można bezpiecznie usunąć: spacje i komentarze.

Zyski z minifikacji
Rys. 4. Zmniejsz i kompresuj JavaScript i CSS

Lighthouse wskazuje tę możliwość w kontroli nieskompresowanego kodu CSS i JavaScriptu. Do procesu kompilacji używaliśmy webpacka, więc aby uzyskać minifikację, po prostu użyliśmy wtyczki Uglify JS.

Kompilacja jest częstym zadaniem, dlatego powinno być możliwe znalezienie gotowego rozwiązania dla dowolnego procesu kompilacji.

Inną przydatną kontrolą w tej sekcji jest Włącz kompresję tekstu. Nie ma powodu, aby wysyłać skompresowane pliki, a większość CDN obsługuje obecnie takie pliki.

Nasz kod był hostowany w Firebase Hosting, a Firebase domyślnie włącza gzipowanie, więc dzięki hostowaniu kodu w przystępnej usłudze CDN otrzymaliśmy to za darmo.

Chociaż gzip jest bardzo popularnym sposobem kompresji, inne mechanizmy, takie jak Zopfli i Brotli, również zyskują na popularności. Brotli obsługuje większość przeglądarek, dlatego możesz użyć pliku binarnego, aby wstępnie skompresować zasoby przed wysłaniem ich na serwer.

Korzystanie z odpowiednich zasad dotyczących pamięci podręcznej

Następnym krokiem było upewnienie się, że nie wysyłamy zasobów dwa razy, jeśli nie jest to konieczne.

Audyt Nieskuteczna polityka dotycząca pamięci podręcznej w Lighthouse pomógł nam zauważyć, że możemy zoptymalizować strategie dotyczące pamięci podręcznej, aby osiągnąć ten cel. Ustawienie na serwerze nagłówka max-age expiration header zapewniło, że podczas powtórnej wizyty użytkownik może ponownie użyć zasobów, które zostały wcześniej pobrane.

W idealnej sytuacji warto przechowywać w pamięci podręcznej jak najwięcej zasobów przez jak najdłuższy czas i zapewnić tokeny walidacyjne, aby można było efektywnie ponownie weryfikować zaktualizowane zasoby.

Usuwanie nieużywanego kodu

Jak dotąd usunęliśmy oczywiste części niepotrzebnego pobierania, ale co z tymi mniej oczywistymi? Może to być na przykład nieużywany kod.

Pokrycie kodu w Narzędziach deweloperskich
Rys. 5. Sprawdzanie pokrycia kodu

Czasami w naszych aplikacjach znajduje się kod, który nie jest niezbędny. Zdarza się to szczególnie wtedy, gdy korzystasz z aplikacji przez dłuższy czas, zmienia się Twój zespół lub Twoje zależności, a czasem zdarza się, że biblioteka pozostała w stanie niepowołanym. Właśnie tak się stało.

Na początku do szybkiego tworzenia prototypów aplikacji używaliśmy biblioteki Material Components. Z czasem przeszliśmy na bardziej niestandardowy wygląd i w ogóle zapomnieliśmy o tej bibliotece. Na szczęście kontrola zasięgu kodu pomogła nam odkryć go na nowo w pakiecie.

Statystyki pokrycia kodu możesz sprawdzić w DevTools, zarówno w przypadku czasu wykonywania, jak i czasu ładowania aplikacji. Na dolnym zrzucie ekranu widać 2 duże czerwone paski – ponad 95% nieużywanych plików CSS i spora ilość kodu JavaScript.

Lighthouse wykrył ten problem również w audycie nieużywanych reguł CSS. Potencjalna oszczędność wynosi ponad 400 KB. Wróciliśmy więc do kodu i usunęliśmy z biblioteki kod JavaScript i CSS.

Jeśli usuniemy adapter MVC, nasze style zmniejszą się do 10 KB.
Rys. 6. Jeśli odrzucimy adapter MVC, nasze style będą zajmować 10 KB!

Dzięki temu pakiet CSS zmniejszył się 20-krotnie, co jest całkiem dobrym wynikiem jak na mały, 2-wierszowy commit.

Oczywiście spowodowało to wzrost naszej skuteczności, a także znacznie poprawiło czas do interakcji.

W przypadku takich zmian samo sprawdzanie danych i wyników nie wystarczy. Usuwanie kodu nigdy nie jest pozbawione ryzyka, dlatego zawsze należy zwracać uwagę na potencjalne regresje.

Nasz kod nie był używany w 95% przypadków – nadal jest 5%. Wygląda na to, że jeden z naszych komponentów nadal używał stylów z tej biblioteki – małe strzałki na suwaku z doodle. Ponieważ była tak mała, mogliśmy ją ręcznie dodać do przycisków.

Przyciski zostały zepsute przez brak biblioteki
Rys. 7. Jeden komponent nadal używał usuniętej biblioteki.

Jeśli usuniesz kod, upewnij się, że masz odpowiedni proces testowania, który pomoże Ci chronić się przed potencjalnymi regresjami wizualnymi.

Unikaj ogromnych ładunków sieciowych

Wiemy, że duże zasoby mogą spowalniać wczytywanie stron internetowych. Mogą one kosztować naszych użytkowników pieniądze i mieć duży wpływ na ich plany danych, dlatego warto o tym pamiętać.

Lighthouse wykrył, że wystąpił problem z niektórymi danymi sieciowymi, korzystając z audytu Enormous network payload.

Wykrywanie ogromnych ładunków sieciowych
Rys. 8. Wykrywanie ogromnych danych sieciowych

W tym przypadku mieliśmy ponad 3 MB kodu, który był przesyłany. To całkiem sporo, zwłaszcza na urządzeniach mobilnych.

Na samym szczycie tej listy Latarnia wskazała, że mamy pakiet dostawcy JavaScripta, który zajmuje 2 MB nieskompresowanego kodu. Jest to też problem podkreślony przez webpack.

Mówiąc najprościej: najszybsze żądanie to te, którego nie ma.

W idealnej sytuacji powinieneś mierzyć wartość każdego zasobu, który udostępniasz użytkownikom, mierzyć skuteczność tych zasobów i decydowować, czy warto udostępnić je w ramach początkowego wrażenia. Czasami te zasoby mogą być opóźnione, ładowane z opóźnieniem lub przetwarzane w czasie bezczynności.

W naszym przypadku mieliśmy do czynienia z dużą liczbą pakietów JavaScript, więc mieliśmy szczęście, ponieważ społeczność JavaScript ma bogaty zestaw narzędzi do sprawdzania pakietów JavaScript.

sprawdzanie pakietu JavaScript,
Rys. 9. Weryfikowanie pakietu JavaScript

Zaczęliśmy od analizy pakietu webpack, która wykazała, że zawieramy zależność o nazwie unicode, która zajmowała 1,6 MB przeanalizowanego kodu JavaScriptu, czyli całkiem sporo.

Następnie skorzystaliśmy z wtyczki importu kosztów do kodu wizualnego, by zwizualizować koszt każdego importowanego modułu. Dzięki temu mogliśmy sprawdzić, który komponent zawiera kod odwołujący się do tego modułu.

Następnie przełączyliśmy się na inne narzędzie, BundlePhobia. W tym narzędziu możesz wpisać nazwę dowolnego pakietu NPM i zobaczyć, jaki jest jego szacowany rozmiar skompresowany i skompresowany do pliku gzip. Znaleźliśmy świetną alternatywę dla używanego przez nas modułu slug, który ważył tylko 2,2 KB, więc go zastąpiliśmy.

Miało to duży wpływ na nasze wyniki. Dzięki tej zmianie i innym odkrytom udało nam się zmniejszyć rozmiar pakietu JavaScript o 2,1 MB.

Po uwzględnieniu pakietów skompresowanych w postaci plików gzip i zminifikowanych rozmiarów zaobserwowaliśmy ogólną poprawę ich wyników o 65%. Przekonaliśmy się, że warto to zrobić.

Zasadniczo postaraj się wyeliminować niepotrzebne pobieranie ze swoich witryn i aplikacji. Sporządzenie spisu komponentów i zmierzenie ich wpływu na skuteczność może mieć naprawdę ogromne znaczenie, dlatego pamiętaj, aby regularnie przeprowadzać audyt komponentów.

Skrócenie czasu uruchamiania JavaScriptu dzięki dzieleniu kodu

Choć duże ładunki sieciowe mogą mieć duży wpływ na naszą aplikację, to jeszcze jedna rzecz to JavaScript.

JavaScript to najdroższy komponent. Jeśli na urządzeniu mobilnym wysyłasz duże pakiety kodu JavaScript, może to opóźnić czas, po którym użytkownicy będą mogli wchodzić w interakcję z elementami interfejsu. Oznacza to, że mogą klikać elementy interfejsu, ale nic istotnego się nie stanie. Dlatego ważne jest, abyśmy zrozumieli, dlaczego kod JavaScript jest tak drogi.

W ten sposób przeglądarka przetwarza JavaScript.

Przetwarzanie kodu JavaScript
Rys. 10. Przetwarzanie kodu JavaScript

Najpierw musimy pobrać skrypt. Mamy silnik JavaScript, który musi przeanalizować kod, skompilować go i wykonać.

Obecnie te etapy nie zajmują dużo czasu na zaawansowanych urządzeniach, np. na komputerach i laptopach, a nawet w zaawansowanych telefonach komórkowych. Jednak na przeciętnym telefonie komórkowym ten proces może potrwać od 5 do 10 razy dłużej. To opóźnia interaktywność, dlatego ważne jest, abyśmy spróbowali skrócić ten czas.

Aby pomóc Ci wykrywać te problemy z aplikacją, wprowadziliśmy w Lighthouse nową kontrolę czasu uruchamiania JavaScriptu.

Czas uruchamiania JavaScriptu
Rys. 11. Kontrola czasu uruchamiania JavaScriptu

W przypadku aplikacji Oodle pokazała nam, że uruchamianie JavaScriptu zajęło 1,8 sekundy. Okazało się, że importowaliśmy statycznie wszystkie nasze ścieżki i komponenty do jednego monolitycznego pakietu JavaScript.

Jednym ze sposobów na obejście tego ograniczenia jest podział kodu.

Dzielenie kodu na fragmenty jest jak pizza

Dzielenie kodu polega na tym, że zamiast dostarczać użytkownikom całej pizzy w postaci kodu JavaScript, dostarczasz im tylko jeden kawałek naraz, gdy tego potrzebują.

Podział kodu można zastosować na poziomie trasy lub komponentu. Świetnie współpracuje z React i React Loadable, Vue.js, Angular, Polymer, Preact i wieloma innymi bibliotekami.

Wprowadziliśmy w naszej aplikacji podział kodu, przechodząc z importów statycznych na importy dynamiczne, co pozwoliło nam asynchronicznie wczytywać kod w miarę potrzeby.

Dzielenie kodu za pomocą importów dynamicznych
Rys. 13. Dzielenie kodu za pomocą importów dynamicznych

Dzięki temu udało nam się zmniejszyć rozmiar pakietów, a także skrócić czas uruchamiania kodu JavaScript. Trwał do 0,78 sekundy, dzięki czemu aplikacja działała o 56% szybciej.

Ogólnie, jeśli tworzysz zaawansowane środowisko JavaScript, wysyłaj kod tylko do użytkowników, których potrzebują.

Korzystaj z takich koncepcji jak dzielenie kodu i sprawdzanie pomysłów takich jak usuwanie zbędących elementów z drzewa. Jeśli używasz webpack, zajrzyj do repozytorium webpack-libs-optimizations, aby dowiedzieć się, jak zmniejszyć rozmiar biblioteki.

Zoptymalizuj obrazy

Żart z wydajnością wczytywania obrazu

W aplikacji Oodle używamy wielu obrazów. Niestety Lighthouse był znacznie mniej entuzjastyczny niż my. Tak naprawdę wszystkie 3 audyty dotyczące obrazów nie powiodły się.

Nie zoptymalizowaliśmy obrazów, nie ustawiliśmy ich prawidłowego rozmiaru i mogliśmy uzyskać lepsze wyniki, gdybyśmy użyli innych formatów obrazów.

audyty obrazów,
Rys. 14. Audyty obrazów w Lighthouse

Zaczęliśmy od optymalizacji obrazów.

W przypadku pojedynczej optymalizacji możesz użyć narzędzi wizualnych, takich jak ImageOptim czy XNConvert.

Bardziej zautomatyzowane podejście polega na dodaniu do procesu tworzenia kroku optymalizacji obrazów za pomocą bibliotek takich jak imagemin.

Dzięki temu obrazy dodane w przyszłości będą automatycznie optymalizowane. Niektóre sieci CDN, np. Akamai, oraz rozwiązania innych firm, takie jak Cloudinary, Fastly i Uploadcare, oferują kompleksowe rozwiązania do optymalizacji obrazów. Możesz też po prostu hostować swoje obrazy w tych usługach.

Jeśli nie chcesz tego robić ze względu na koszty lub problemy z opóźnieniem, możesz skorzystać z alternatywnych rozwiązań hostowanych lokalnie, takich jak Thumbor czy Imageflow.

Przed i po optymalizacji
Rys. 15. Przed i po optymalizacji

Nasz obraz tła PNG został oznaczony w webpack jako duży, i słusznie. Po prawidłowym dopasowaniu go do obszaru widocznego i przesłaniu do ImageOptim zmniejszyliśmy rozmiar do 100 KB, co jest dopuszczalne.

Powtórzenie tego procesu w przypadku wielu obrazów w naszej witrynie pozwoliło nam znacznie zmniejszyć całkowitą wagę strony.

Używanie odpowiedniego formatu dla treści animowanych

GIF-y mogą być naprawdę drogie. Co zaskakujące, format GIF nigdy nie był przeznaczony do tworzenia animacji. Dlatego przejście na bardziej odpowiedni format wideo pozwala zaoszczędzić duże oszczędności związane z rozmiarem pliku.

W aplikacji Oodle używaliśmy GIF-a jako sekwencji wprowadzającej na stronie głównej. Według Lighthouse możemy zaoszczędzić ponad 7 MB, przechodząc na wydajniejszy format wideo. Nasz klip ważył około 7,3 MB, czyli zdecydowanie za dużo jak na potrzeby zwykłej witryny, więc zamiast tego przekształciliśmy go w element wideo z 2 plikami źródłowymi – mp4 i WebM, aby zapewnić większą obsługę przez przeglądarki.

Zastępowanie animowanych GIF-ów filmami
Rys. 16. Zastąp animowane GIF-y filmami

Za pomocą narzędzia FFmpeg przekonwertowaliśmy animowane GIF-y do pliku MP4. Format WebM pozwala na jeszcze większe oszczędności – interfejs API ImageOptim może przeprowadzić taką konwersję za Ciebie.

ffmpeg -i animation.gif -b:v 0 -crf 40 -vf scale=600:-1 video.mp4

Dzięki tej konwersji udało nam się zmniejszyć ogólną wagę o ponad 80%. Dzięki temu udało nam się zmniejszyć rozmiar pliku do około 1 MB.

Mimo to 1 MB to duża ilość danych, zwłaszcza dla użytkowników z ograniczoną przepustowością. Na szczęście mogliśmy użyć interfejsu Effective Type API, aby wykryć, że użytkownicy mają wolne łącze, i zamiast tego wyświetlić im znacznie mniejszy plik JPEG.

Ten interfejs wykorzystuje efektywny czas błądzenia i wartości pobierania do oszacowania typu sieci, z której korzysta użytkownik. Zwraca on po prostu ciąg znaków: slow 2G, 2G, 3G lub 4G. W zależności od tej wartości, jeśli użytkownik korzysta z mniej niż 4G, możemy zastąpić element wideo obrazem.

if (navigator.connection.effectiveType) { ... }

W pewnym stopniu ogranicza to wygodę korzystania, ale przynajmniej strona jest dostępna przy wolnym połączeniu.

Leniwe ładowanie obrazów poza ekranem

Karuzele, suwaki i bardzo długie strony często wczytują obrazy, mimo że użytkownik nie może ich od razu zobaczyć na stronie.

Lighthouse odnotuje to zachowanie w audycie obrazów poza ekranem, a możesz je też zobaczyć w panelu sieci w Narzędziach dla programistów. Jeśli widzisz wiele obrazów, a na stronie widocznych jest tylko kilka z nich, być może warto rozważyć ich wczytywanie opóźnione.

Łagodne wczytywanie nie jest jeszcze obsługiwane natywnie w przeglądarce, więc musimy użyć JavaScriptu, aby dodać tę funkcję. Skorzystaliśmy z biblioteki Lazysizes, aby dodać leniwe ładowanie coverów Oodle.

<!-- Import library -->
import lazysizes from 'lazysizes'  <!-- or -->
<script src="lazysizes.min.js"></script>

<!-- Use it -->

<img data-src="image.jpg" class="lazyload"/>
<img class="lazyload"
    data-sizes="auto"
    data-src="image2.jpg"
    data-srcset="image1.jpg 300w,
    image2.jpg 600w,
    image3.jpg 900w"/>

Lazysizes jest inteligentny, ponieważ nie tylko śledzi zmiany widoczności elementu, ale też proaktywnie pobiera z wyprzedzeniem elementy, które znajdują się w pobliżu widoku, aby zapewnić użytkownikom optymalne wrażenia. Zapewnia też opcjonalną integrację z IntersectionObserver, która umożliwia bardzo wydajne wyszukiwanie widoczności.

Po tej zmianie nasze obrazy są pobierane na żądanie. Jeśli chcesz dowiedzieć się więcej na ten temat, odwiedź stronę images.guide – to bardzo przydatne i wyczerpujące źródło informacji.

Pomóż przeglądarce w wcześniejszym dostarczaniu kluczowych zasobów

Nie wszystkie bajty przesyłane do przeglądarki mają ten sam stopień ważności, a przeglądarka o tym wie. Wiele przeglądarek korzysta z heurystyki, aby określić, co pobrać w pierwszej kolejności. Czasami pobierają one pliki CSS przed obrazami lub skryptami.

Coś, co może być przydatne, to my, autorzy strony, informujący przeglądarkę, co jest dla nas naprawdę ważne. Na szczęście w ostatnich latach dostawcy przeglądarek dodali kilka funkcji, które nam w tym pomagają, np. wskazówki dotyczące zasobów, takie jak link rel=preconnect, preload lub prefetch.

Te możliwości, które zostały udostępnione na platformie internetowej, pomagają przeglądarce pobierać odpowiednie dane we właściwym czasie. Mogą one być nieco wydajniejsze niż niektóre niestandardowe metody wczytywania oparte na logice, które są wykonywane za pomocą skryptu.

Zobaczmy, jak Lighthouse pomaga nam skutecznie korzystać z niektórych z tych funkcji.

Pierwszą rzeczą, którą Lighthouse zaleca, jest unikanie wielokrotnych kosztownych podróży powrotnych do dowolnego miejsca pochodzenia.

Unikaj wielu, kosztownych lotów w obie strony do dowolnego punktu początkowego
Rys. 17. Unikaj wielokrotnych, kosztownych podróży w obie strony do dowolnego miejsca.

W przypadku aplikacji Oodle intensywnie korzystamy z Google Fonts. Gdy dodasz do strony arkusz stylów Google Fonts, zostanie on połączony z 2 subdomenami. Lighthouse informuje, że jeśli uda nam się rozgrzać to połączenie, możemy skrócić czas początkowego nawiązywania połączenia nawet o 300 milisekund.

Korzystając z linku rel preconnect, możemy skutecznie ukryć opóźnienie połączenia.

Szczególnie w przypadku Google Fonts, gdzie pliki czcionek CSS są hostowane na stronie googleapis.com, a zasobów czcionek na stronie Gstatic, może to mieć bardzo duży wpływ. Po zastosowaniu tej optymalizacji udało nam się skrócić czas o kilkaset milisekund.

Kolejną rzeczą, która sugeruje, jest wstępne wczytywanie kluczowych żądań.

Wstępne wczytywanie żądań kluczy
Rys. 18. Wstępne wczytywanie żądań kluczy

Funkcja <link rel=preload> jest bardzo zaawansowana, informuje przeglądarkę o tym, że potrzebny jest zasób w bieżącej nawigacji, i stara się ją pobrać tak szybko, jak to możliwe.

Teraz Lighthouse informuje nas, że powinniśmy zacząć i wstępnie wczytać najważniejsze zasoby czcionek internetowych, ponieważ wczytujemy 2 czcionki internetowe.

Wstępne wczytywanie przy użyciu czcionki internetowej wygląda tak: podaj rel=preload, przekazujesz as z typem czcionki, a następnie określasz (np. woff2) typ czcionki, który chcesz wczytać.

Wpływ tej zmiany na Twoją stronę jest naprawdę wyraźny.

Wpływ wstępnego wczytywania zasobów
Rys. 19. Wpływ wstępnego wczytania zasobów

Jeśli czcionki internetowe są kluczowe dla Twojej strony, a nie używasz linku rel preload, przeglądarka musi najpierw pobrać kod HTML, przeanalizować kod CSS, a dopiero potem pobrać czcionki internetowe.

Dzięki wstępnemu wczytywaniu linków z poziomu linku, gdy tylko przeglądarka przeanalizuje kod HTML, może zacząć pobierać czcionki internetowe znacznie wcześniej. W przypadku naszej aplikacji udało nam się w ten sposób skrócić czas potrzebny na renderowanie tekstu za pomocą czcionek internetowych o 1 sekundę.

Jeśli chcesz wczytywać czcionki za pomocą Google Fonts, nie jest to tak proste.

Adresy URL czcionek Google, które podajemy w naszych stylach w przypadku czcionek, są dość regularnie aktualizowane przez zespół ds. czcionek. Te adresy URL mogą wygasnąć lub zostać zaktualizowane w regularnych odstępach czasu. Dlatego, jeśli chcesz mieć pełną kontrolę nad wczytywaniem czcionek, zalecamy samodzielne hostowanie czcionek internetowych. Może to być bardzo przydatne, ponieważ daje Ci dostęp do takich funkcji jak wstępny wczytywanie linków.

W naszym przypadku bardzo przydatne okazało się narzędzie Google Web Fonts Helper, które pomogło nam w pracy offline z tymi czcionkami internetowymi i ich konfiguracji na komputerze.

Niezależnie od tego, czy używasz czcionek internetowych jako kluczowych zasobów, czy też są to zasoby JavaScript, postaraj się pomóc przeglądarce w jak najszybszym wczytaniu tych zasobów.

Experimental: Priority Hints

Mamy coś specjalnego do przekazania. Oprócz funkcji takich jak wskazówki dotyczące zasobów i wstępny odczyt pracowaliśmy nad zupełnie nową eksperymentalną funkcją przeglądarki, którą nazywamy wskazówkami priorytetowymi.

Ustaw priorytet początkowo widocznych treści
Rys. 20. Wskazówki dotyczące priorytetów

To nowa funkcja, która pozwala zasugerować przeglądarce, jak ważny jest zasób. Wyświetla nowy atrybut – „Ważność” – z wartościami „Niski”, „Wysoki” lub „Automatycznie”.

Dzięki temu możemy wskazać obniżenie priorytetu mniej ważnych zasobów, takich jak style niekrytyczne, obrazy lub pobieranie wywołań interfejsu API, aby zmniejszyć rywalizację. Możemy też zwiększyć priorytet ważnych elementów, takich jak obrazy główne.

W przypadku aplikacji Oodle udało się to znaleźć w jednym praktycznym miejscu, w którym możemy zoptymalizować ustawienia.

Ustawianie priorytetu początkowo widocznej treści
Rys. 21. Ustaw priorytet dla początkowo widocznych treści.

Zanim dodaliśmy do obrazów ładowanie opóźnione, przeglądarka pobierała wszystkie obrazy z karuzeli z doodle, przypisując im wysoki priorytet. Niestety to obrazy znajdujące się w środku karuzeli były dla użytkownika najważniejsze. W związku z tym ustawiliśmy priorytet tych obrazów tła na bardzo niski, a obrazów na pierwszym planie – na bardzo wysoki. W efekcie udało nam się skrócić czas pobierania i renderowania tych obrazów o 2 sekundy w porównaniu z siecią 3G. To bardzo pozytywne doświadczenie.

Mamy nadzieję, że za kilka tygodni udostępnimy tę funkcję w wersji Canary.

mieć strategię wczytywania czcionek internetowych;

Typografia jest podstawą dobrego projektu. Jeśli używasz czcionek internetowych, nie blokuj renderowania tekstu i nie wyświetlaj niewidocznego tekstu.

Wskazujemy na to w Lighthouse za pomocą audytu Unikaj niewidocznego tekstu podczas wczytywania czcionek internetowych.

Unikaj niewidocznego tekstu podczas wczytywania czcionek internetowych
Rys. 22. Unikaj niewidocznego tekstu podczas wczytywania czcionek internetowych.

Jeśli ładujesz czcionki internetowe za pomocą bloku kroju czcionki, pozwalasz przeglądarce zdecydować, co zrobić, jeśli pobranie danej czcionki będzie się długo zajmować. Niektóre przeglądarki czekają na to do 3 sekund, po czym przełączają się na czcionkę systemową, a potem zastępują ją czcionką pobieraną z sieci.

Staramy się unikać tego niewidocznego tekstu, więc w tym przypadku nie moglibyśmy zobaczyć klasycznych doodli z tego tygodnia, gdyby czcionka internetowa trwała zbyt długo. Na szczęście dzięki nowej funkcji font-display masz znacznie większą kontrolę nad tym procesem.

    @font-face {
      font-family: 'Montserrat';
      font-style: normal;
      font-display: swap;
      font-weight: 400;
      src: local('Montserrat Regular'), local('Montserrat-Regular'),
          /* Chrome 26+, Opera 23+, Firefox 39+ */
          url('montserrat-v12-latin-regular.woff2') format('woff2'),
            /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
          url('montserrat-v12-latin-regular.woff') format('woff');
    }

Wyświetlanie czcionek pozwala określić, jak czcionki internetowe będą renderowane lub zastępowane na podstawie czasu ich przełączania.

W tym przypadku używamy wymiany wyświetlania czcionki. Zamiana powoduje ustawienie 0-sekundowego okresu bloku i nieskończonego okresu zamiany. Oznacza to, że jeśli czcionka będzie się długo ładować, przeglądarka zapisze tekst z użyciem czcionki zastępczej. I zamiennie zastosuje czcionkę, gdy ta będzie dostępna.

W przypadku naszej aplikacji było to świetne, ponieważ pozwoliło nam wyświetlać tekst już na bardzo wczesnym etapie i przechodzić na czcionkę internetową, gdy tylko była gotowa.

Wyświetlanie czcionki
Rys. 23. Wyświetlanie czcionki

Jeśli używasz czcionek internetowych, tak jak większość użytkowników internetu, zastosuj dobrą strategię ładowania czcionek internetowych.

Istnieje wiele funkcji platformy internetowej, których możesz używać do optymalizacji wczytywania czcionek. Zapoznaj się też z repozytorium Web Font Recipes Zacha Leathermana, ponieważ jest ono naprawdę świetne.

Ogranicz skrypty blokujące renderowanie

Są też inne części aplikacji, które możemy przesunąć do wcześniejszego etapu łańcucha pobierania, aby zapewnić użytkownikom przynajmniej podstawowe funkcje na nieco wcześniejszym etapie.

Na pasku czasowym Lighthouse widać, że przez pierwsze kilka sekund, gdy wczytują się wszystkie zasoby, użytkownik nie widzi żadnych treści.

Zmniejsz możliwości arkuszy stylów blokujących renderowanie
Rys. 24. Zmniejsz możliwość użycia arkuszy stylów blokujących renderowanie

Pobieranie i przetwarzanie zewnętrznych arkuszy stylów uniemożliwia nam dalszy postęp w procesie renderowania.

Możemy spróbować zoptymalizować ścieżkę renderowania krytycznego, przekazując niektóre style nieco wcześniej.

Jeśli wyodrębnimy style odpowiedzialne za to początkowe renderowanie i umieścimy je w dokumentie HTML, przeglądarka będzie mogła je od razu renderować bez czekania na zewnętrzne arkusze stylów.

W naszym przypadku użyliśmy modułu NPM o nazwie Krytyczne, aby umieścić kluczowe treści w pliku index.html na etapie kompilacji.

Choć ten moduł wymagał od nas najwięcej wysiłku, trudno było ze wszystkim tak sobie poradzić.

Jeśli nie będziesz uważać lub struktura witryny będzie bardzo złożona, wprowadzenie tego typu wzoru może być bardzo trudne, jeśli od początku nie zaplanowano architektury powłoki aplikacji.

Dlatego tak ważne jest, aby już na wczesnym etapie zwracać uwagę na skuteczność. Jeśli nie od razu projektujesz witryny pod kątem wydajności, istnieje duże prawdopodobieństwo, że później wystąpią problemy.

Ostatecznie ryzyko się opłaciło, udało nam się to zrobić, a aplikacja zaczęła wyświetlać treści znacznie wcześniej, co znacznie skróciło czas pierwszego wyrenderowania elementu znaczącego.

Wynik

To była długa lista optymalizacji skuteczności, które zastosowaliśmy w naszej witrynie. Przyjrzyjmy się wynikom. Tak nasza aplikacja wczytywała się na średnim urządzeniu mobilnym w sieci 3G – przed optymalizacją i po niej.

Wynik wydajności Lighthouse wzrósł z 23 na 91. To całkiem niezły postęp, jeśli chodzi o szybkość. Wszystkie zmiany zostały wprowadzone dzięki ciągłemu sprawdzaniu i śledzeniu raportu Lighthouse. Jeśli chcesz sprawdzić, jak technicznie wdrożyliśmy wszystkie ulepszenia, zapoznaj się z naszym repozytorium, zwłaszcza z przesłanymi tam PR-ami.

Prognozowanie wydajności – tworzenie doświadczeń użytkowników na podstawie danych

Jesteśmy przekonani, że systemy uczące się to ekscytująca przyszłość w wielu dziedzinach. Mamy nadzieję, że w przyszłości więcej eksperymentów będzie się opierać na prawdziwych danych, które mogą naprawdę wpływać na wrażenia użytkowników.

Obecnie podejmujemy wiele arbitralnych decyzji dotyczących tego, czego użytkownik może chcieć lub czego może potrzebować, a także tego, co warto pobrać z poziomu pamięci podręcznej lub podręcznej. Jeśli się nie pomylimy, możemy nadać priorytet niewielkiej liczbie zasobów, ale trudno jest to zastosować do całej witryny.

Mamy już dostępne dane, które pozwolą nam lepiej optymalizować kampanie. Dzięki interfejsowi Google Analytics Reporting API możemy sprawdzić, które adresy URL w naszej witrynie mają najwyższy współczynnik odrzuceń i najwyższy współczynnik odrzuceń na stronie. Dzięki temu możemy wyciągać wnioski na temat zasobów, którym powinniśmy nadać priorytet.

Jeśli połączymy to z dobrym modelem prawdopodobieństwa, unikniemy marnowania danych użytkownika przez agresywne nadmierne pobieranie treści. Możemy wykorzystać te dane z Google Analytics do wdrożenia takich modeli, korzystając z systemów uczących się i modeli, np. łańcuchów Markowa czy sieci neuronowej.

Pakiety oparte na danych w przypadku aplikacji internetowych
Rys. 25. Pakiety oparte na danych w przypadku aplikacji internetowych

Aby ułatwić przeprowadzanie tych eksperymentów, ogłaszamy nową inicjatywę o nazwie Guess.js.

Guess.js
Rys. 26. Guess.js

Guess.js to projekt skupiający się na wrażeniach użytkowników w internecie opartych na danych. Mamy nadzieję, że zainspiruje on do korzystania z danych dostępnych w Google Analytics do poprawy wydajności witryny i nie tylko. Wszystkie te narzędzia są dostępne na GitHubie jako oprogramowanie open source. Ta funkcja została opracowana we współpracy z komunitą open source przez Minko Gecheva, Kyle’a Matthewsa z Gatsby, Katie Hempenius i kilku innych osób.

Sprawdź Guess.js i powiedz nam, co o nim myślisz.

Podsumowanie

Wyniki i dane pomagają zwiększać szybkość działania stron internetowych, ale są one tylko środkiem, a nie celem samym w sobie.

Wszyscy znamy to uczucie, gdy strona wczytuje się wolno, ale teraz mamy możliwość zapewnienia użytkownikom szybszego wczytywania stron.

Poprawianie wydajności to proces. Wiele drobnych zmian może przynieść duże korzyści. Korzystając z odpowiednich narzędzi do optymalizacji i śledząc raporty Lighthouse, możesz zapewnić użytkownikom lepsze i bardziej wszechstronne wrażenia.

Szczególne podziękowania dla: Warda Peetersa, Minko Gecheva, Kyle’a Mathewsa, Katie Hempenius, Doma Farolina, Yoava Weissa, Susie Lu, Yusuke Utsunomiya, Toma Ankersa, Lighthouse i Google Doodles.