Now that I have been using this concept for a couple of months (having put countless additional hours and days into improving it further) I think I have reached a point where I would like to share my insights and findings with you.
My interest in the aforementioned matter basically became an obsession when Chris Coyier from css-tricks.com gave me the opportunity to have my article about REMux (a concept about a solely rem based approach to responsive web design) published on his site. I knew I wanted my long planned (but by that time not even started) personal website to be online when the article got published and I wanted it to also make use of REMux as well as all the other nice little to medium things I had been developing before. The resulting amount of "pressure" was both, frustrating and very helpful. By the moment I really started I knew I would manage to keep up the pace and make the site something I could be proud of.
My personal advantage in this case is that I am not only a frontend developer but have been developing backend solutions ever since. The one thing I learnt over the years is that focussing yourself on keeping things lean, modular and separated almost guarantees a more versatile solution, even for future challenges yet unkown.
So I ended up having a quite flexible and modular "toolbox" already at hand when starting to practically work on the idea I had in mind.
The other (external) parts of my technological foundation were
- require.js: for all the lazy-loading needs of modules
- handlebars.js: to be able to transfer the template logic to frontend modules as well
With the help of my already existing library in combination with require.js and handlebars.js I quickly developed a fully functional prototype where I was able to load everything as separate modules. After this proof of concepts had worked out quite well I started implementing further optimizations as mentioned above.
Lazy-Loading wherever possible
The prototype I developed still had the limitation to load everything immediately after the page was loaded. Performance-wise it actually made things far worse than before by having a lot more server requests due to using require.js. Luckily I had already developed two jQuery plugins that came in really handy to solve part of the problem:
- qoopido.emerge: to react on HTML elements entering, leaving or nearing the currently visible area of the document
- qoopido.lazyimage: to specifically lazy-load images based on qoopido.emerge events
This lazy-load principle is also applied to some more elements on my site:
- Disquss comment box below any article
- Full list of GitHub repositories on some pages
- Contact form and all of its functionality
In addition to lazy-loading there are some more techniques I use to improve the overall user experience and to reduce the perceived loading time. If you have a look at the slideshow on my homepage you will see that it also follows the modular loading scheme described in the previous paragraph but adds a new layer when it comes to loading the actual images. Normally a slideshow would be expected to load all images concurrently when its HTML is added to the DOM. In this case the loading of images is queued in addition so that the images are loaded sequentially one after the other (microdata for the win). So the image first shown to the visitor will get requested first and therefore be loaded earlier than having requested all of the images at once.
Another layer of optimization is my qoopido.shrinkimage jQuery plugin which can be used to reduce the file size of transparent PNGs by about 60-80% by converting them to a proprietary JSON format that separates the alpha channel from the color information and stores the latter as JPEG with variable compression. qoopido.shrinkimage is able to re-combine the JSON-data loaded via HTML5 canvas element and should gracefully fallback to the original PNG for browsers without canvas support. The plugin is, for example, used within the headphone visual widget on my homepage although it may not be as obvious.
Last but not least I set up a separate CDN domain for all my static assets. Beside setting a rather long expires header it also suppresses any kind of cookies to further reduce response times. To make sure that any visitor still gets the latest version of any asset I also incorporated a directory structure that allows the use of version numbers and included it in my Grunt build process. For very common external resources like jQuery and jQuery easing I stayed with CDNJS to profit even more fromt its performance gain.
As stated in the abstract of this article any possible and helpful kind of minification should always be used self-evidently and, in an ideal world, happen completely automatic and transparent for the developer. What I did to achieve the latter two points is that I directly incorporated the necessary tools into my build process.
By the time of the development I am describing here I already used LESS for writing CSS paired with LESS.app (I am on OSX) to watch and compile the CSS files whenever needed. LESS is not only helpful for minification but allows you to work on a CSS structure that is easier to understand and maintain than pure vanilla CSS.
The only (partly) manual optimization I frequently use is ImageOptim. ImageOptim is an OSX application on which you can drag & drop any kind of image or multiple images. It will try different standalone optimizers (with adjustable parameters) and save the resulting image with the smallest file size.
What you have read up to now might sound rather complex and very difficult but rest assured that most of it was (only) combining well known tools & techniques into something new and capable to fit my needs. The first version (more or less the prototype with added layout) of the site was developed in only a couple of days and than got refined over time. My personal learnings were immense and I am absolutely sure they are worth the effort several times!
What I would love to see is more people realizing that a kind of "performance culture" will be needed in the near future and already makes sense today, regardless of whatever that means for the individual :)
Keep in mind that this article represents my personal way of approaching the matter but does not claim to be the holy grail for everyone!