Performance - Part one

how do we go from this:

or this?

To this?

or even this?

optimization checklist

  • Optimize images
  • Eliminate render-blocking JavaScript and CSS in above-the-fold content
  • Leverage browser caching
  • Minify JavaScript
  • Minify CSS
  • Minify HTML
  • Enable compression

optimize images

  • Use the right compression
  • Responsive images: srcset, x and w descriptors
  • Picture-element
  • Verify which image is served from DevTools

eliminate render-blocking js + CSS

  • Use a small inline style-block for above-the-fold content if possible OR use a small, initial CSS file
  • Load scripts just before </body>
  • Load with async or defer when possible
  • Load CSS and webfonts from script
<style>
	body {
		font-family: sans-serif;
	}
	.g20, .g25, .g33, .g50, .g66, .g75, .g100 {
		float: left;
		display: flex;
	}
	.g20 { width: 50%; }
	.g25 { width: 50% }
	.g33 { width: 100% }
	.g50 { width: 100%; }
	.g66 { width: 66.666666%; }
	.g75 { width: 75%; }
	.g100 { width: 100%; }
	@media only screen and (min-width:769px){
		.g20{ width: 20%; }
		.g25 { width: 25%; }
		.g33 { width: 33.333333%; }
		.g50 { width: 50%; }
	}
	.inner {
		display: flex;
		flex-flow: row wrap;
		max-width: 1024px;
		margin: 0 auto;
		padding: 0 10px;
	}
</style>
<script async>
	function loadCSS(sURL) {
		var
		oLink = document.createElement("link");
		oLink.setAttribute("rel", "stylesheet");
		oLink.setAttribute("href", sURL);
		oLink.setAttribute("type", "text/css");
		document.body.appendChild(oLink);
	}
	loadCSS("/assets/css/styles.css");
	loadCSS("//fonts.googleapis.com/css?family=Montserrat:700|Oswald:400,300,700|Questrial|Roboto:300,400");
</script>

leverage browser caching

  • Set Cache-Control headers
  • Set ETags (Validaton tokens)

minify javascript

  • Use uglify or other minifier from Gulp

minify css

  • Set outputStyle="compressed" in gulp-sass

minify html

  • If serving a static site, do it from Gulp
  • Sitecore and epiServer can do it ... but it might not be worth it ...

Enable compression

  • Gzip everything above a specified threshold, could be 1024 kb
  • DON'T gzip files which are already compressed, they will end up bigger than the original files
  • If using a CDN, pre-gzip and set correct mime-type and Content-Encoding-header to "gzip"
  • If using IIS, add SVG mime-type

example: deploy to s3 from gulp

function gzipS3(sPrefix, sPath) {
	return gulp.src(sPath)
	.pipe(gzip(GZIP_OPTIONS))
	.pipe(rename(function(filePath) {
		filePath.dirname = path.join(sPrefix, filePath.dirname);
	}))
	.pipe(s3(AWS_SETTINGS, AWS_OPTIONS))
	.pipe(debug())
	.pipe(gulp.dest("./gzip/"));
}

caching and offline

a short intro to pwa's

Psst! Go to the "Application"-tab in DevTools

the manifest

{
	"short_name": "Blocks!",
	"name": "Blocks! UI Elements",
	"description": "A collection of UI elements and patterns",
	"icons": [
		{
			"src": "/assets/favicon/android-chrome-192x192.png",
			"sizes": "192x192",
			"type": "image/png"
		},
		{
			"src": "/assets/favicon/android-chrome-512x512.png",
			"sizes": "512x512",
			"type": "image/png"
		}
	],
	"start_url": "/",
	"dir": "ltr",
	"lang": "en-US",
	"display": "standalone",
	"orientation": "any",
	"background_color": "white",
	"theme_color": "cornflowerblue"
}

the serviceworker

var CACHE_NAME = "blocks-cache-v1";
var CACHED_URLS = [
"/offline.html",
"/assets/css/styles.css",
"/assets/favicon/favicon.ico"
];

self.addEventListener("install", function(event) {
  event.waitUntil(
    caches.open(CACHE_NAME).then(function(cache) {
      return cache.addAll(CACHED_URLS);
    })
  );
});

self.addEventListener("fetch", function(event) {
  event.respondWith(
    fetch(event.request).catch(function() {
      return caches.match(event.request).then(function(response) {
      if (response) {
        return response;
      } else if (event.request.headers.get("accept").includes("text/html")) {
        return caches.match("/offline.html");
      }
...

USING SW-precache

gulp.task("sw", function(callback) {
	swPrecache.write("./serviceworker.js", {
		staticFileGlobs: [
			"*.{html,js}",
			"files.json",
			"assets/**/*.*",
			"feature/**/*"
		]
	}, callback);
});

indexeddb

function getDB() {
  if (!oDB) {
    oDB = new Promise(function (resolve, reject) {
    var oRequest = window.indexedDB.open(oSettings.DB, oSettings.Version);
    oRequest.onerror = function () {
      reject(oRequest.error);
    };

    oRequest.onupgradeneeded = function () {
      var oStore = oRequest.result.createObjectStore(oSettings.Store);
      oStore.createIndex("timestamp", "timestamp");
    };

    oRequest.onsuccess = function () {
      resolve(oRequest.result);
...

fine-tuning

adding resource hints

<link rel="dns-prefetch" href="//example.com">
<link rel="preconnect" href="//example.com">
<link rel="prefetch" href="image.png">
<link rel="prerender" href="//example.com">
<link rel="preload" href="image.png">

minifying html?

probably not worth it

29kb HTML ~ 2.42kb gzipped

27kb HTML ~ 2.26kb gzipped

http2, SERVICEWORKER

advice editors ... again

(10 mb gif not allowed)

Performance - Part One

By Mads Stoumann

Performance - Part One

  • 47