Optymalizacja krytycznej ścieżki renderowania

Szybkość wczytywania strony internetowej ma ogromne znaczenie dla odbiorcy naszych treści. Zmierzono, że wraz ze wzrostem szybkości ładowania witryny, użytkownicy chętniej korzystają ze strony, spędzają na niej więcej czasu i ma ona mniejszy współczynnik odrzuceń, co w przypadku np. e-commerce wpływa bezpośrednio na większe zyski. Osobiście odczuwam duży dyskomfort korzystając z serwisów, które wczytują się wolno i raczej nie jestem osamotniony w tym odczuciu. Doskonały opis problemu przedstawia Google na swojej oficjalnej stronie. W tym artykule skupię się na praktycznych wskazówkach, które pozytywnie wpływają na szybkość ładowania stron i ostatecznie – zwiększenie konwersji, bo w gruncie rzeczy o to właśnie chodzi. Nie zawsze wprowadzenie tych zmian jest proste, zwłaszcza w dużych serwisach, gdzie architektura może narzucać pewne ograniczenia. Dlatego najwięcej zyskamy, kiedy o optymalizacji pomyślimy już na etapie projektowania strony.

Optymalizacja Treści – czyli jak łatwo przyspieszyć stronę

Zanim przejdziemy do konkretów, kila słów na temat optymalizacji treści na stronie. Przed wprowadzeniem poprawek technicznych w kodzie naszej strony, zastanówmy się czy aby na pewno wszystkie wczytywane przez nas zasoby są odpowiednio zoptymalizowane i konieczne.

Na tym etapie warto zastawnowić się czy nie wysyłamy użytkownikowi zbędnych obrazów, stylów CSS, skryptów JS, czy kodu HTML. Piszę o tym, bo zdarza się to często, a największy zysk osiągniemy, po prosu eliminując takie zasoby.

Do tego punktu zaliczamy także:

  • kompresję danych (np. GZIP)
  • minifikację kodu HTML, CSS, JS
  • optymalizację obrazów
  • optymalizację czcionek
  • Buforowanie HTTP

Przy okazji warto zastanowić się nad odpowiednim podziałem plików CSS i JS. Analizując style pierwszej strony z brzegu, możemy zauważyć, że ich duża część zwyczajnie nie jest wykorzystywana, dlatego zamiast ładować jeden wspólny plik CSS na wszystkich podstronach, można wydzielić z niego mniejsze, odpowiadające np. za wygląd widoku kategorii i ładować je tylko gdy są potrzebne. W przypadku webowych aplikacji napisanych w JS, podział taki jest nawet istotniejszy, bo oprócz pobrania zasobów, przeglądarka musi przetworzyć i wykonać kod, co jest jeszcze bardziej zasobożerne. Na szczęście dzięki narzędziom takim jak Webpack w prosty sposób można zautomatyzować ten proces. Zadbanie o prawidłową optymalizację powyższych obszarów jest podstawową kwestią, o którą powinniśmy zadbać i z pewnością znacząco przyspieszy to działanie naszej strony. Ale to nie wszystko co możemy zrobić.

Optymalizacja Krytycznej Ścieżki Renderowania

Wyzwanie przed jakim stoi przeglądarka, to pobranie i interpretacja zasobów, w celu wyświetlenia treści w pożądany przez nas sposób. Proces ten odbywa się w kilku krokach i dopiero po jego zakończeniu strona zostanie wyświetlona. Z punktu widzenia wygody użytkownika, który chce jak najszybciej dotrzeć do treści, stanowi to oczywiście przeszkodę. Dlatego jako osoby odpowiedzialne, za ten ważny aspekt życia odbiorcy, powinniśmy poświęcić temu zagadnieniu nieco uwagi.

Style CSS Blokujące Renderowanie

Zewnętrze arkusze CSS są zasobem, który domyślnie blokuje renderowanie strony. Wynika to bezpośrednio z mechanizmu działania przeglądarki, która wstrzymuje renderowanie treści, w celu utworzenia tzw. drzewa renderowania. Dopiero po jego utworzeniu przeglądarka będzie „wiedziała” jak wyświetlić naszą stronę. Warto w tym momencie zaznaczyć, że sposób w jaki piszemy reguły CSS, wpływa na czas trwania tego procesu. W celu jego skrócenia warto stosować możliwie najprostsze reguły CSS opisujące wygląd strony. Jednym z przykładów podejścia pozwalającego na optymalizację czasu tworzenia drzewa jest metodologia BEM. To czy nasza strona posiada zasoby blokujące renderowanie strony możemy zobaczyć np. za pomocą narzędzia Pagespeed lub bezpośrednio analizując Timeline za pomocą Chrome DevTools.

Rozwiązaniem powyższego problemu jest jak najszybsze przesłanie kodu CSS użytkownikowi, w celu skrócenia czasu renderowania. Dla arkuszy stylów nieprzekraczających kilkuset linii możemy to osiągnąć poprzez umieszczenie kodu CSS bezpośrednio w nagłówku strony wewnątrz znacznika style. Niestety, zazwyczaj mamy do czynienia z kodem CSS, który przekracza rozmiarem kilka tysięcy linii. Co w takim przypadku?

Wyodrębnienie Krytycznego Kodu CSS

Jednym z pomysłów na skrócenie czasu renderowania w przypadku bardzo dużych arkuszy stylów, jest wyodrębnienie krytycznego kodu CSS, który jest niezbędny przeglądarce do pierwszego wyrenderowania strony. Innymi słowy, w nagłówku strony umieszczamy wyłącznie style potrzebne do wyświetlenia części strony widocznej na ekranie.

Jeżeli zaczynamy nowy projekt, wówczas, obok głównego arkusza stylów, możemy utworzyć osobny, zwierający wyłącznie krytyczny kod CSS, którego zawartość ostatecznie umieszczamy w nagłówku wewnątrz znacznika style.

Gdy takie rozwiązanie nie wchodzi w grę lub zależy nam na czasie, z pomocą przychodzą narzędzia takie jak HtmlCriticalWebpackPlugin, które pozwalają na wyodrębnienie „krytycznego kodu”, z istniejącego pliku CSS.

Asynchroniczne Wczytywanie Arkuszy Stylów

Wyodrębnienie krytycznego kodu i „wstrzyknięcie” go do nagłówka, to dopiero początek, ale dzięki temu zabiegowi strona po załadowaniu będzie zawierać style wyłącznie dla części widocznej na ekranie, co pozwala nam na opóźnienie doczytywania pozostałego kodu w sposób, który nie będzie blokował renderowania. Możemy takie rozwiązanie zaimplementować sami, jednak w świecie modularnego JavaScriptu, prawie wszystko mamy podane na tacy. Dlatego po raz kolejny odwołam się do już istniejącego narzędzia, którym jest loadCSS. Narzędzie to zostało stworzone właśnie w celu asynchronicznego ładowania kodu CSS, który nie jest krytyczny dla pierwszego renderowania strony.

W pierwszej kolejności autorzy zalecają skorzystanie ze sposobu, który jest zaimplementowany w nowszych przeglądarkach. Wykorzystując tag link wraz z atrybutem preload, możemy poinstruować przeglądarkę, które zasoby są dla nas istotne i ma je wczytać z wysokim priorytetem. Jest to o tyle ważne, że nadal naszym priorytetem jest szybkie załadowanie i dołączenie pozostałego kodu CSS. W przeciwnym razie użytkownik, co prawda szybko wyświetli naszą stronę, ale przewijając ją w dół, może napotkać nieostylowane elementy. Przykładowy kod do umieszczenia w nagłówku strony, wygląda następująco:

<link rel="preload" href="path/to/style.css" as="style">

Podczas ładowania strony, plik zostanie pobrany w tle. Jednak na tym kończy się rola tego tagu, o poprawne dołączenie pobranego pliku musimy zadbać sami:

<link rel="preload" href="path/to/mystylesheet.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="path/to/mystylesheet.css"></noscript>

Na koniec zadbajmy również o prawidłowe wczytywanie arkusza stylów w przeglądarkach nieobsługujących tej metody. W tym celu posłużymy się wyżej wymienionym skryptem loadCSS oraz polyfillem dla atrybutu rel=”preload”. Najnowszą wersję tego skryptu możemy znaleźć tutaj.

<link rel="preload" href="path/to/mystylesheet.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="path/to/mystylesheet.css"></noscript>
<script>
/*! loadCSS rel=preload polyfill. [c]2017 Filament Group, Inc. MIT License */
(function(){ ... }());
</script>