Krótki wpis o tym jak Google niszczy konkurencję

5 minut(y)

Żyję sobie spokojnie jak gdyby nigdy nic i nagle cerrato z 4programmers.net pisze do mnie, że moja strona mi się rozjechała i podsyła screena. Patrzę i faktycznie plik CSS się w ogóle nie wczytał. Sprawdzam w Chromium, Chromie, Operze i Edge i wszędzie działa. Pytam się cerrato co za niszową przeglądarkę używa.

Firefox, a wszystkie przeglądarki w których testowałem są oparte na silniku z Chromium. Chyba już tylko Firefox ma niespokrewniony silnik.

Analiza

Często analizuję swoją statyczną stronę generowaną przez Jekylla w PageSpeed Insights. Ostatnio Google stwierdził, że źle robię wczytując blokująco swoje CSSy i dał mi poradę z preload.

W rezultacie moje ładowanie pliku CSS przed uruchomieniem jekylla wygląda następująco:

  <link rel="preload" href="{{ site.baseurl }}/assets/css/style.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
  <noscript>
    <link rel="stylesheet" href="{{ site.baseurl }}/assets/css/style.css">
  </noscript>

Co ostatecznie daje następujący kod html:

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

Czyli jeśli przeglądarka ma włączony JavaScript to plik CSS jest ładowany asynchronicznie. W przeciwnym wypadku jest wykonane klasyczne ładowanie synchroniczne. Niestety Google nie powiedział, że preload nie jest wspierany przez Firefox :(

Dlaczego Firefox nie wspiera preload? Bo preload nie jest w standardzie, ale jest w drafcie standardu i jest duża szansa, że będzie. Moim skromnym zdaniem Firefox nie nadąża lub ogranicza zasoby na programistów.

Rozwiązanie

Rozwiązaniem na szybko jest oczywiście usunięcie preload z kodu HTML. Nie jest to oczywiście rozwiązanie zadowalające.

Na szczęście istnieje skrypt loadCSS, który rozwiązuje wszystkie moje problemy. W rezultacie moje ładowanie pliku CSS przed uruchomieniem jekylla wygląda następująco:

  <link rel="preload" href="{{ site.baseurl }}/assets/css/style.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
  <noscript>
    <link rel="stylesheet" href="{{ site.baseurl }}/assets/css/style.css">
  </noscript>
  <script type="text/javascript">
   {% include head/loadCss.js %}
  </script>

A po uruchomieniu jekylla dostaję:

  <link rel="preload" href="/assets/css/style.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
  <noscript>
    <link rel="stylesheet" href="/assets/css/style.css">
  </noscript>
  <script type="text/javascript">
   /* loadCSS. [c]2017 Filament Group, Inc. MIT License */
/* This file is meant as a standalone workflow for
- testing support for link[rel=preload]
- enabling async CSS loading in browsers that do not support rel=preload
- applying rel preload css once loaded, whether supported or not.
*/
(function( w ){
	"use strict";
	if( !w.loadCSS ){
		w.loadCSS = function(){};
	}
	var rp = loadCSS.relpreload = {};
	rp.support = (function(){
		var ret;
		try {
			ret = w.document.createElement( "link" ).relList.supports( "preload" );
		} catch (e) {
			ret = false;
		}
		return function(){
			return ret;
		};
	})();

	rp.bindMediaToggle = function( link ){
		var finalMedia = link.media || "all";

		function enableStylesheet(){
			if( link.addEventListener ){
				link.removeEventListener( "load", enableStylesheet );
			} else if( link.attachEvent ){
				link.detachEvent( "onload", enableStylesheet );
			}
			link.setAttribute( "onload", null );
			link.media = finalMedia;
		}

		if( link.addEventListener ){
			link.addEventListener( "load", enableStylesheet );
		} else if( link.attachEvent ){
			link.attachEvent( "onload", enableStylesheet );
		}

		setTimeout(function(){
			link.rel = "stylesheet";
			link.media = "only x";
		});
		setTimeout( enableStylesheet, 3000 );
	};

	rp.poly = function(){
		if( rp.support() ){
			return;
		}
		var links = w.document.getElementsByTagName( "link" );
		for( var i = 0; i < links.length; i++ ){
			var link = links[ i ];
			if( link.rel === "preload" && link.getAttribute( "as" ) === "style" && !link.getAttribute( "data-loadcss" ) ){
				link.setAttribute( "data-loadcss", true );
				rp.bindMediaToggle( link );
			}
		}
	};

	if( !rp.support() ){
		rp.poly();

		var run = w.setInterval( rp.poly, 500 );
		if( w.addEventListener ){
			w.addEventListener( "load", function(){
				rp.poly();
				w.clearInterval( run );
			} );
		} else if( w.attachEvent ){
			w.attachEvent( "onload", function(){
				rp.poly();
				w.clearInterval( run );
			} );
		}
	}


	if( typeof exports !== "undefined" ){
		exports.loadCSS = loadCSS;
	}
	else {
		w.loadCSS = loadCSS;
	}
	console.log("Loaded CSS");
}( typeof global !== "undefined" ? global : this ) );

  </script>

Podsumowanie

  • Nie ufaj Googlowi, niszczą konkurencję
  • Warto testować strony na Firefoxie