W dzisiejszej części cyklu zajmiemy się tematem arkuszy stylów definiujących wygląd strony internetowej. Postaram się w bardzo skrócony sposób przedstawić, jak serwuje się przeglądarce kod CSS, co przeglądarka z nim robi i czy można tu zastosować jakieś optymalizacje wydajności strony WWW.
Trudno wyobrazić sobie nowoczesną stronę internetową bez kodu CSS. I chociaż w początkach istnienia języka HTML występował szereg komend i słów sterujących, pozwalających definiować wygląd strony, to jednak bardzo szybko zostały one wyparte przez język określający wygląd elementów na stronie.
Banalnym wydaje się zastosowanie arkuszy stylów przy budowie dowolnej strony internetowej. Wszak wystarczy podpiąć do struktury dokumentu HTML plik ze stylami, dopisać kilka słów kluczowych opisujących wygląd danego elementu – i już można mówić o prostej stronie internetowej.
I w swojej prostocie tak to właśnie miało wyglądać, jednak nowoczesne strony internetowe składają się z tysięcy linii kodu określających sam wygląd!
Wśród sposobów, w jaki można załączyć style do dokumentu HTML tak, aby były one interpretowane przez przeglądarki, możemy wyróżnić trzy: przez załączenie stylów bezpośrednio w kod HTML za pomocą składni style=’tutaj kolejne atrybuty’, poprzez dodanie do nagłówka (sekcji head) stylu CSS zamkniętego w elemencie składni <style> oraz za pomocą linkowania w sekcji head do pliku zawierającego kod CSS.
Ten ostatni został przyjęty za jedyny słuszny sposób, a dwa pozostałe są uznawane za tak zwany inline-style i powinny być unikane za wszelką cenę – nawet przeglądarki internetowe zwracają uwagę na obecność zbyt dużej ilości kodu CSS bezpośrednio w HTML.
I tutaj można zadać sobie pytanie: dlaczego inline-style jest tak piętnowany? Przecież każdy sposób aby dostarczyć przeglądarce informacje na temat wyglądu elementów strony jest dobry. Problemem jednak jest jego lokalność. O ile kod CSS dostarczony w nagłówku strony daje nam jakąś elastyczność, tak serwowanie stylów bezpośrednio z poziomu elementów składni HTML sprawia, że bardzo trudno nadpisać później taki styl, a to powoduje niepotrzebny rozrost kodu CSS. Chęć zmiany wyglądu pociąga za sobą konieczność przejrzenia całego serwisu w poszukiwaniu elementów, które chcemy przekształcić, co zwiększa koszty utrzymania strony. Już tylko te dwa przykłady uznawane są za złe praktyki i dlatego unika się korzystania z inline-style.
Z punktu widzenia wydajności, inline-style może być jednak uznawany za najszybszy sposób dostarczenia informacji o wyglądzie danego elementu. Wszak przesyła się go wraz z dokumentem HTML, a on jest pobierany jako pierwszy.
Ciekawą informacją jest również fakt, iż przeglądarka w momencie wykrycia w dokumencie HTML pliku CSS blokuje proces renderowania strony (przygotowania przez przeglądarkę wyglądu strony celem jej wyświetlenia użytkownikowi) dopóki nie pobierze i nie zinterpretuje całego pliku CSS.
Takie a nie inne zachowanie przeglądarki podyktowane jest faktem, że dopóki nie wiemy, jak ma wyglądać dany element na stronie, przeglądarka nie wyświetli tego elementu użytkownikowi, bo sam proces rysowania na ekranie jest dosyć kosztowny obliczeniowo. I jeżeli przeglądarka ma wyświetlić niebieski przycisk tylko po to, aby za chwilę na samym dole interpretowanego pliku CSS odszukać zmianę jego koloru na czerwony, to jest to nieoptymalne zarządzanie zasobami komputera, bo wymaga ponownego przerysowania elementu.
Fakt blokowania renderowania strony aż pobierze się cały plik daje się we znaki w przypadku bardzo dużych stron internetowych. Wtedy trzeba pobrać cały plik CSS, a w nim niewykorzystana zostanie zdecydowana większość reguł. Rozbijanie arkuszy stylów też nie jest wyjściem, bo im więcej przeglądarka musi pobrać plików, tym wolniej zostanie wyświetlona strona. Dodatkowo, z wcześniejszego wpisu wiemy już, że w praktyce szybciej jest pobrać jeden duży plik niż 10 mniejszych.
Czy w takim razie skazani jesteśmy na nieefektywne zarządzanie arkuszami stylu przez przeglądarki?
Wchodząc na dowolną stronę, najpierw widzimy jej górną część, a reszta jest ukryta i wymaga scrollowania, żeby ją wyświetlić. Istnieje pojęcie above the fold, oznaczające całą treść, która wyświetlana jest na ekranie monitora bez konieczności przejścia na dół strony poprzez jej scrollowanie.
Cały kod CSS użyty do wyświetlenia części strony above the fold uznawany jest za tak zwany critical CSS. Gdyby udało się przeglądarce dostarczyć w jak najszybszy sposób tylko ten fragment kodu CSS, przeglądarka mogłaby zyskać cenny czas zanim odbiorca zdąży zareagować, aby pobrać resztę strony. W teorii oczywiste rozwiązanie jakie przychodzi do głowy jest wręcz banalnie proste – wycinamy fragment pliku CSS i wklejamy go, dajmy na to, jako inline style w części nagłówkowej strony.
W rzeczywistości prawie każda strona w dzisiejszych realiach powstaje przy pomocy frameworka do stylów, który jest silnym fundamentem składającym się z tysięcy linii kodu i zapewniającym możliwie najlepsze środowisko pracy dla developera oraz „klocki”, z których można budować nawet zaawansowane strony. Z takiego gąszczu kodu bardzo trudno wyodrębnić krytyczny CSS, a nawet jeśli się uda, okazuje się, że jest on na tyle duży, że nie warto wklejać go bezpośrednio w strukturę strony.
Keith Clark w swoim wpisie „Loading CSS without blocking render” zademonstrował technikę, która polega na wymuszeniu na przeglądarce pobrania pliku CSS bez oczekiwania na przeanalizowanie całego pliku – tym samym bez blokowania renderowania strony. Sam sposób realizacji tego zadania jest bardzo ciekawy i pozwala zoptymalizować wyświetlanie stron na każdej popularnej przeglądarce internetowej, stosując jednocześnie wyjście awaryjne, jeżeli przeglądarka się pogubi w swoim działaniu. Na czym to polega?
Otóż Keith sugeruje, aby podzielić swój plik z kodem CSS na dwa pliki. Ten najważniejszy, zawierający krytyczny kod CSS, należy umieścić standardowo w nagłówku dokumentu HTML. Natomiast w tak zwanej „stopce” strony, czyli w ostatnich linijkach kodu dokumentu, umieścić powinno się drugi plik CSS zawierający mniej ważny, ale dopełniający styl strony arkusz stylów.
Należy to zrobić w ten sposób:
<link rel=”stylesheet” href=”css.css” media=”none” onload=”if(media!=’all’)media=’all'”>
<noscript><link rel=”stylesheet” href=”css.css”></noscript>
Otóż podlinkowujemy do dokumentu HTML plik CSS, ale jednocześnie ustawiamy atrybut media na „none”, który mówi przeglądarce, że ten styl nie ma zastosowania do niczego. Keith odkrył, że przeglądarki tak podlinkowany plik CSS pobierają, ale w sposób nie blokujący rysowania strony.
Następnie, jeżeli plik został pobrany i atrybut media jest inny niż „all”, a my chcemy, aby kod ten CSS został jednak zinterpretowany przez przeglądarkę za pomocą javascriptu, wymusza się, by atrybut był ustawiony na „all” – w ten sposób umożliwiając przeglądarce zastosowanie stylów zapisanych w pliku.
Użycie tej techniki powoduje, że może być widoczne małe przeładowanie strony, na której elementy wcześniej wystylizowanie dostają czasami zupełnie inny wygląd. Jednak ta niedogodność widoczna jest tylko przy pierwszym załadowaniu strony – każde przejście na kolejną podstronę czy ponowna wizyta po krótkim czasie nie spowodują widocznego mignięcia stylu strony.
Jeżeli jednak przeglądarka nie obsługuje javascriptu albo użytkownik wyłączył jego obsługę na stronie, istnieje wyjście awaryjne w postaci załadowania pliku CSS w tradycyjny sposób.
Co można zyskać na tej optymalizacji strony? Keith w swoim artykule przedstawił test, w którym udało mu się na połączeniu 3G uzyskać redukcję czasu ładowania strony z 2.8 sekundy do 2 sekund. Czas oczekiwania na początek ukazywania treści został także skrócony – z początkowych 1.8 sekundy do 1.4 sekundy.