Showing posts with label browserify. Show all posts
Showing posts with label browserify. Show all posts

Thursday, 18 January 2018

Lazy loading stylesheets using LoadCSS

In my last post, I talked about adding stylesheets into my Gulp file, part of my new development process.  The follow on to this for me was thinking about whether all of those stylesheets were really needed up front.  As I explained in that post, concatenating them and minifying them will certainly reduce the overall filesize, and the number of TCP connections (and therefore time), but what if some of this could be delayed until after the page had even loaded?

This is often referred to as lazy loading.  The Filament Group have created a great plugin for this called loadCSS, which can be found on NPM as fg-loadcss.  Their description of why you should be using it goes like this...
Referencing CSS stylesheets with link[rel=stylesheet] or @import causes browsers to delay page rendering while a stylesheet loads. When loading stylesheets that are not critical to the initial rendering of a page, this blocking behavior is undesirable. The new <link rel="preload">standard enables us to load stylesheets asynchronously, without blocking rendering, and loadCSS provides a JavaScript polyfill for that feature to allow it to work across browsers. Additionally, loadCSS offers a separate (and optional) JavaScript function for loading stylesheets dynamically.

The "preload" option is not well supported at all currently, so for the time being at least, a polyfill of this nature is definitely required.

As I'm already using Browserify in my javascript Gulp task, this is really easy to add into my javascript file.  Obviously I need to first install the fg-loadcss package, and then I can add the following lines of javascript...
  var loadcss = require("fg-loadcss");
  var reflink = $("head").children("link[rel=stylesheet]").get(-1);
  loadcss.loadCSS("https://fonts.googleapis.com/css?family=Indie+Flower",reflink);


This first requires the package (which Browserify will pull in), then finds a reference element (it will insert the <style> tag directly after this one) and then it calls the manual "loadCSS" function with the path to the stylesheet.  In my example, this is to a Google Font file.

This manual method is actually not part of Filament Groups recommended workflow, but I prefer it, as it keeps the code neat in my opinion, and runs it after my javascript is running, which I think is best for non-critical styles.  If you look on their Github repo, they do give other example usage though.

You can then remove the reference to the stylesheet from the <head> section, or better yet, move it to the very bottom of your page with a no-javascript fallback, like this...
<noscript><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Indie+Flower"></noscript>

This means that even if javascript is disabled, your stylesheet (or font, in my case) will still load, which is great!

I didn't get the chance to cover this in my Optimising your website: A development workflow with Git and Gulp course on Skillshare, but I hope to add it into a future course.

Wednesday, 10 January 2018

Minifying (or uglifying) javascript

Totally in keeping with my New Year's Resolution, here is a lovely new blog post!  And it's a continuation of my new development process.  The last post in the series detailed my switch to using pump instead of pipe in my Gulp task.

Today's post is about minifying javascript, which is sometimes called "uglifying".  The reason being that this beautifully crafted javascript snippet...
//jquery wrapped anonymous function
$(function() {
  //clickjacking protection
  if(self!==top) {
    top.location = self.location; //break out of frame
  }
});

...gets minified to this monstrosity...
$(function(){self!==top&&(top.location=self.location)});

As you can see, the comments and whitespace have all been stripped, as well as converting the if statement to the shorthand variant - a number of operations are performed as part of this process.

So why on earth would we want to do this, I hear you cry.  Well essentially because it makes the file size smaller (in some cases, considerably smaller).  In the example above, the character count has gone from 166 to 56, which is approximately a third of the size, but with exactly the same functionality.  This makes it both quicker and cheaper (if you're on any kind of data plan, such as a mobile phone) to download the file for the user, and also saves you bandwidth costs.  This can be better than gzip compression for some files, but even better yet, do both!

If you want to read further on why you should do this, you can read this blog post relating to Drupal.

The best way to achieve this is to use the gulp-uglify package. So I installed this plugin and then included it at the top of my Gulp file...
var uglify = require("gulp-uglify");

I then took the task that I wrote about in my last blog post in this series and added the highlighted line...
gulp.task("js",function(cb) {
  pump([ 
    browserify(jsFiles).bundle(),
    source("script.js"),
    uglify(), 
    gulp.dest("build/js")
      ],cb); 
    });

This doesn't actually work, so not quite that simple.  The problem is to do with the way that Gulp can handle files as streams or buffers.  In this case the browserify plugin outputs the files as streams, but the gulp-uglify package requires them as buffers.  

Luckily this is easily fixed using a plugin called vinyl-buffer, which once included like so...
var buffer = require("vinyl-buffer");

...can be added to the task so that it looks like this...

gulp.task("js",function(cb) {
  pump([ 
    browserify(jsFiles).bundle(),
    source("script.js"),
    buffer(),  
    uglify(), 
    gulp.dest("build/js")
      ],cb); 
    });

Now we have a process which minifies our files, improving the performance and user experience, as well as reducing bandwidth costs - win win!

This is also covered in my Skillshare course, Optimising your website: A development workflow with Git and Gulp.  The relevant video is 11 - Minifying Javascript Files.

Monday, 10 July 2017

Adding Browserify into the mix

Having already written my first gulp task, I was ready to get a bit more advanced.  My first task was to concatenate javascript files, including putting my local version of jQuery in front of my own javascript, so there was a single file to download, with all the required plugins before.  However, I'd already heard of Browserify, and wanted to see if this would work with Gulp (short answer - yes!).

Browserify is a tool which lets you "require" plugins in your javascript, and then processes these at build time (for example, in a Gulp task) to automatically include the plugins as well.  This would achieve the same end result as my current concatenation task, but without me having to keep and manually update local copies of the plugin files, as I could get Browserify to do the job of finding and including them.

I went through a few different iterations, all of which worked but were a bit messy for one reason or another (including using the gulp-browserify plugin, which is deprecated!) but I'm going to skip over those and skip straight to my final implementation.

So I needed to include a copy of extra plugins at the top of my Gulp file...
var browserify = require("browserify");
var source = require("vinyl-source-stream");

Then I updated my task to look this this...
gulp.task("js",function(cb) {
  browserify(jsFiles).bundle(),
    .pipe(source("script.js"))
    .pipe(gulp.dest("build/js"));
});

Notice that I am no longer using Gulp.Src to pick up the javascript files from the array, Browserify is now handling this, and I'm using the "bundle" method to concatenate them, instead of using gulp-concat.  During this "bundle" method, the javascript is also being processed to find any "require" statements.

For me, I needed to require jQuery and a few other bits, so at the top of my project javsacript file, I added the following lines...

var jQuery = require("jquery");
window.$ = window.jQuery = jQuery;
require("jquery.easing");
require("bootstrap/js/scrollspy");

As you can see, you can simply use the "require" method by itself, or you can assign the exported method to a variable.  In the case of jQuery, it needs to be stored globally in order for Bootstrap to load properly.  

I also went from including all of the Bootstrap library from a CDN to just including the component that I required, which saves on filesize!

It's now really easy to include new libraries and plugins into my javascript, by simply installing them using NPM, and including them using Browserify, as part of my automated Gulp task.