Have you ever had one of those moments when you enter the URL of a webpage, it Enter and the only thing you get is a loading spinner? You have full signal, other pages load normally, but nothing happens on this one... What do you? You'll probably close that tab and try again later. Or maybe you'll never come back.

Time matters. And when it comes to getting the users attention you have a very short time span. According to Ilya Grigorik, on his "Speed, Performance, and Human Perception" keynote, you only have one second. One second! After that first second there's a mental context switch - in the user's minds it has been so long that they start to think about other things. And after about 10 seconds, the user is already bored and will probably close the tab.

What I will cover here is a set of tips that you can implement on your website to improve the loading speeds, and I'm going to use my own webpage as an example.

First tests

The first thing we are going to do is to test the performance of the site. I went to the Google Developers, and on the products searched for PageSpeed Tools. In there there's a analysis tool called PageSpeed Insights that will check your online site and will give you a score based on some rules (minified code, page structure, server configurations, etc.).

Here's the first results I got. For mobile:

First analysis on mobile

And for desktop:

First analysis for desktop

There is also another test available called Mobile-Friendly Test, but here everything was OK.

Mobile-Friendly Test

This tool will show you where you have space for improvements on your webpage and what you should do to make it faster. That's our next step.

Improve performance

There are two approaches to improve your website performance and reach a 100% score.

  • You can use online tools
  • You can use web development tools on your machine

The first approach it's the easiest one. Grab all your files, use the online tools to minify them and send them to the server. Done.
Well... not quite. What if you have 50 files to minify? Or 200? And what if you change your code? Are you going to repeat that task over and over?

That's why I'll cover the second approach. We'll use Gulp, a JavaScript task runner, that will do all that work for us. If you don't know how to use Gulp feel free to check this blog post where I give a brief intro about it and the installation requirements.

Setup

The first thing to do is to setup a working enviroment, so I grabed all the page files and moved them to a folder I called "src".

Move your data inside src folder

Now, we need to install Gulp globally (you'll need Node and NPM installed on your machine). Just open your terminal and type

$ npm install --global gulp

Then go to your project folder and install it in your development dependencies.

$ npm install --save-dev gulp

After that you'll see a new folder called "node_modules". That's where Gulp and all your Node modules will be stored.

After installing Gulp

There's just one more step before running Gulp. We need to set a configuration file so that Gulp knows what to do with your files, and that is easy as creating a file called "gulpfile.js" on your root folder. Here's the contents of it:

var gulp = require('gulp');
gulp.task('default', function () {});

Right now isn't doing anything, but you can run it. Just type

$ gulp

and you should get this output:

[14:12:14] Starting 'default'...
[14:12:14] Finished 'default' after 72 μs

Now that we have all set, the next step is to create the Gulp tasks to do all the work for us like minification, concatenation, etc..

Minify

OK, let's get something done with Gulp! First task: minifying our files.

The minifying process what it does basically is to strip all white-spaces and carriage returns, and it switches variable names to shorter ones in order to get a very small file while preserving all the functionality.

I wasn't completely honest with you in the setup section, because in order to do the minification we'll need to install the node modules that will help us with that task. So we need to install the gulp-minify-css for minifying our CSS files and gulp-uglify for the JS files.

$ npm install --save-dev gulp-minify-css
$ npm install --save-dev gulp-uglify

Now that we have our required modules we have to create the tasks for each one. We can actually do everything inside our default task, but I prefer to do a separate task for each type of file. This way we'll keep our code cleaner, easier to read and maintain, and, if we want, we can run the task for the CSS or the JS files separately.

Let's add our minify-css and gulp-uglify modules to the top of our gulpfile.js.

var minifycss = require('gulp-minify-css');
var uglify = require('gulp-uglify');

We can then start creating our tasks that will read all the files inside the "src" folder, minify and export to another folder. All our processed files will go inside this folder called "dist" that in the end of the process will have all our files ready to deploy on the server.

// CSS
gulp.task('css', function() {
  return gulp.src('src/css/*.css')
    .pipe(minifycss())
    .pipe(gulp.dest('dist/css'));
});

// JS
gulp.task('js', function() {
  return gulp.src('src/js/*.js')
    .pipe(uglify())
    .pipe(gulp.dest('dist/js'));
});

Running gulp again on your terminal will create a new folder called "dist", with the css and js folders inside that will contain our minified files.

Concatenate

Sending down the wire just a couple of files instead a bunch of them makes the whole process of loading a page must faster.
So what we're going to do next is to merge all our files into one single and minified file; one for the all the CSS and another for all the JS. And for that we'll use the modules gulp-concat-css and gulp-concat.

$ npm install --save-dev gulp-concat-css
$ npm install --save-dev gulp-concat

We're using different modules because gulp-concat-css is more appropriate for CSS files since it does the rebase of the URLs automatically.

Gulp is a stream-based build system, so if you want to apply another process after that, you just keep piping. Both modules require a string argument with the name of the concatenated file, in this case I'm using semeano.min.css and semeano.min.js.
Actually, I'm not using any JS on my webpage, but I'll do it just for the sake of the tutorial ;)

First, let's add our dependencies on the top,

var concatcss = require('gulp-concat-css');
var concat = require('gulp-concat');

then the aditional piping on the tasks.

// CSS
gulp.task('css', function() {
  return gulp.src('src/css/*.css')
    .pipe(concatcss('semeano.min.css'))
    .pipe(minifycss())
    .pipe(gulp.dest('dist/css'));
});

// JS
gulp.task('js', function() {
  return gulp.src('src/js/*.js')
    .pipe(uglify())
    .pipe(concat('semeano.min.js'))
    .pipe(gulp.dest('dist/js'));
});

JSHint

This tool won't actually help reducing the size of the files but it will help you detect errors and potential problems in our JavaScript code. A must-have IMO.

JSHint is a fork from another similar tool called JSLint, so why use JSHint? Basically, it all comes down to what you prefer, but you can check here the reason behind the existance of JSHint.

Once again, let's install it,

$ npm install --save-dev gulp-jshint

add the dependency,

var jshint = require('gulp-jshint');

and add it to our task, before uglifying it.

// JS
gulp.task('js', function() {
  return gulp.src('src/js/*.js')
    .pipe(jshint())
    .pipe(jshint.reporter("default"))
    .pipe(uglify())
    .pipe(concat('semeano.min.js'))
    .pipe(gulp.dest('dist/js'));
});

The reporter line is what will output the warnings on the terminal. We're using the default reporter but you can change it to a different one.

Images & SVGs

When we run PageSpeed Insights it will check if the images that we're using are larger than they need to be. Here we can strip all the metadata from the images which can reduce a bit the file size. For that we'll be using gulp-imagemin and gulp-svgmin for images and SVG files respectively.

$ npm install --save-dev gulp-imagemin
$ npm install --save-dev gulp-svgmin

Here are the tasks for minimizing images and SVG files, I think it's already self-explanatory.

var imagemin = require('gulp-imagemin'),
    svgmin = require('gulp-svgmin');
...

// Images
gulp.task('img', function () {
  return gulp.src('src/img/*.*')
    .pipe(imagemin())
    .pipe(gulp.dest('dist/img'));
});

// SVG
gulp.task('svg', function () {
  return gulp.src('src/svg/*.svg')
    .pipe(svgmin())
    .pipe(gulp.dest('dist/svg'));
});

On my case it didn't actually made a significant change on the image.

gulp-imagemin: Minified 1 image (saved 543 B - 0.2%)

But I remembered that the image was the main issue on the tests:

Main issue

That is happening because I'm using the same image for desktop and mobile. Everything was fine on the desktop tests, but on mobile, since I'm using the original image and resizing it, I'm wasting a lot of pixels.

The solution for this is to have different image sizes for each media query. That way, if you need a 200x200 image, the browser will download only that size, saving bandwidth along the wire.

HTML

Minifying our HTML files is also important and here is where you'll probably see the most decrease in file size. For this we'll be using the gulp-minify-html. Also, in order to make the changes on the resources automatically, from the files on the "src" folder to the "dist" folder, we need to install gulp-useref.

$ npm install --save-dev gulp-minify-html
$ npm install --save-dev gulp-useref

Here's the task:

var minifyhtml = require('gulp-minify-html'),
    useref = require('gulp-useref');
...

// HTML
gulp.task('html', function () {
  return gulp.src('src/*.html')
    .pipe(useref())
    .pipe(minifyhtml())
    .pipe(gulp.dest('dist'));
});

This task requires a small change in the HTML files (in this case there's only one, the index.html). We need to add a special comment for the gulp-useref module referencing a block on resource files (html5reset-1.6.1.css and semeano.css) that will result in a single file (semeano.min.css) after the concatenation and minification.

<!-- build:css css/semeano.min.css -->
<link rel="stylesheet" type="text/css" href="css/html5reset-1.6.1.css" />
<link rel="stylesheet" type="text/css" href="css/semeano.css" />
<!-- endbuild -->

This way, the index.html on the "dist" folder will have just one link tag:

<link rel="stylesheet" type="text/css" href="css/semeano.min.css" />

Note: Now that we have all these tasks declared we need to call them. Just add the created tasks to the default task dependencies, i.e. an array of task names as the second argument:

// Default task
gulp.task('default', ['css', 'js', 'img', 'svg', 'html'], function () {});

What that basically means is that before running the default task, Gulp needs to run all those tasks first.

Conclusions

After these changes we can see some improvement on the score. It seems a small change (only 12%) but after the minification of our files we are sending 33% less KB compared to what was before. That is something! There are although other things left to improve that I'll cover on the second part of this article.

PageSpeed score after 1st part of improvements

Here's some other topics that could help improve the loading speed of your webpages:

  • Use of spritesheets. Instead of downloading a set of images, just but them all toghether inside one single file and use CSS to select the correct area of each image. This solution also applies to SVG files.
  • Convert images to WebP. Is a new image format that provides lossless and lossy compression for images on the web. WebP lossless images are 26% smaller in size compared to PNGs.
  • Inline CSS. If your CSS files are small, just inline them in your HTML. That will save some server requests.

All these tips can be added to your Gulp set of tasks. Checkout this blog post to know what modules to use and other tips to improve your loading speeds.

This concludes the first part of this article, where we focused on what to optimize and using Gulp to automate those tasks.
On the second part I'll give you more tips, where we're going to make some changes on the structure of the HTML code, what are the available solutions to optimize the content delivery, and some server configurations that will give us that final boost!

Feel free to share if you liked it, and also if you have other ideas or tips let me know, I'm always open to learn new stuff! You can reach me out on twitter.