Speed up your website

Introduction

This is small tutorial that covers some tips how you can improve the performance of your website. Many of these steps include tasks that can be handled by a build tool (I use Grunt) so I recommend you using a build tool like that. You can test your sites current performance by using a tool like this one or this one.

Don’t use images when it’s not needed

In the earlier days of web development images were used for all sorts of things; buttons, icons, gradients, backgrounds and even texts for displaying headings and logotypes that required specific fonts. With modern day web technlogies though you can easily recreate all of these things without the use of image files.

  • Buttons: CSS3 introduced lots of interesting attributes that can create effects that could previously only could be achieved by rendering a image in for instance Photoshop and then using the img-tag. Attributes like border-radius for rounded corners, box-shadow for drop shadow effects, text-shadow for text shadow effects, text-stroke for text outline as well as a bunch of neat CSS animations for click/hover/focus events. You can use a library like Bootstrap to get some decent looking default buttons. Here is a nice codepen as an example of what can be achieved with HTML and CSS only.
  • Icons: Instead of using images for small icons consider using a glyph icon library like Bootstrap icons or Fotn awesome. It’s better for performance and also scales better.
  • Gradients: If for whatever reason you wan’t to use gradients you can use a gradient css generator like this one to generate css gradients.
  • Backgrounds: It is possible to create a lot of quite impressive background patterns using CSS only, here are a few examples of these.
  • Texts: With CSS3 you can use custom fonts for texts, so there is no need to create images for headings and logos. Here is a nice tutorial about this. You may also consider using a convenient service like Google fonts for this. Here are a few examples of impressive typographies created using CSS only; Example 1, Example 2, Example 3, Example 4, Example 5, Example 6, Example 7, Example 8

Optimize images

There are obviously still some instances where you must use images, for instance when using photos for hero components and whatnot. When using image files you should remember to optmizie them. You can do this by using a simple service like Optimizilla. You’ll be surprised how much space you can save by optmizing images while not seemingly losing any photo quality at all.

Minify JS

I use the build tool Grunt and the plugin uglify to minify, compress and uglify all my Javascript code. This will reduce the file size making it load faster. Here’s how to set it up using Grunt. First install the plugin (this assumes you already have npm and Grunt setup):

npm install grunt-contrib-uglify --save-dev

Now configure your Gruntfile.js:

module.exports = function(grunt) {
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        uglify: {
            options: {
                banner: '/*! My compressed and uglified JS | <%= grunt.template.today("yyyy-mm-dd") %> */\n'
            },
            build: {
                files: [{
                    src: 'js/**/*.js',
                    dest: 'build/main.min.js'
                }]
            }
        }
    });
    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.registerTask('default', [
        'uglify'
    ]);
};

Now you can compress and uglify your Javascript by just running the default Grunt build task. This will take all js-files (recursively) in your js-folder and compile them into one large js-file. The output will be located in build/main.min.js.

Minify CSS

Minifying CSS is also recommended. I recommend the Grunt plugin grunt-contrib-cssmin. Install it by running:

npm install grunt-contrib-cssmin --save-dev

Personally I don’t use this plugin. Instead I am using the plugin “grunt-contrib-less” (since I am using less as a css postprocessor). This plugin will not only transpile less to css but also minify. However if you are not using less you can use grunt-contrib-cssmin. I add the following configuration to my Gruntfile.js:

module.exports = function(grunt) {
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        uglify: {
            options: {
                banner: '/*! My compressed and uglified JS | <%= grunt.template.today("yyyy-mm-dd") %> */\n'
            },
            build: {
                files: [{
                    src: 'js/**/*.js',
                    dest: 'build/main.min.js'
                }]
            }
        },
        cssmin: {
            minify: {
                src: 'css/default.css',
                dest: 'build/default.min.css'
            }
        }
    });
    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.loadNpmTasks('grunt-contrib-cssmin');
    grunt.registerTask('default', [
        'uglify',
        'cssmin'
    ]);
};

This will take the file css/default.css and minify it, the output will be located in build/default.min.css.

Lazy load CSS

If your website uses very large CSS-files you might wan’t to consider lazy loading your CSS. Or perhaps you could break up your css into a default stylesheet file that can be included normally and one that can be lazyloaded. Simply lazy load css by using the following script:

<script>
var cb = function() {
    var l = document.createElement('link');
    l.rel = 'stylesheet';
    l.href = 'yourCSSfile.css';
    var h = document.getElementsByTagName('head')[0];
    h.parentNode.insertBefore(l, h);
};
var raf = requestAnimationFrame || mozRequestAnimationFrame ||
    webkitRequestAnimationFrame || msRequestAnimationFrame;
if (raf) raf(cb);
else window.addEventListener('load', cb);
</script>

Inline CSS

Another way to speed up your site is to simply inline you CSS. This means that instead of referencing a separate css-file you paste the full css in a style-tag within the head-tag. By doing this your browser won’t have to download the css-file and it reduces the amount of HTTP requests your browser have to make. Obviously it’s not very convenient having to constantly copypaste your css to your markup so I suggest using the grunt plugin grunt-replace for inlining CSS on every build. Simply run:

npm grunt-replace --save-dev

Then modify your Gruntfile.js file so that it looks a bit like this:

module.exports = function(grunt) {
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        uglify: {
            options: {
                banner: '/*! My compressed and uglified JS | <%= grunt.template.today("yyyy-mm-dd") %> */\n'
            },
            build: {
                files: [{
                    src: 'js/**/*.js',
                    dest: 'build/main.min.js'
                }]
            }
        },
        cssmin: {
            minify: {
                src: 'css/default.css',
                dest: 'build/default.min.css'
            }
        },
        replace: {
            inline: {
                options: {
                    patterns: [{
                        match: 'defaultCss',
                        replacement: '<%= grunt.file.read("build/default.min.css") %>'
                    }]
                },
                files: [{
                    src: ['head.html'],
                    dest: 'build/head.html'
                }]
            }
        }
    });
    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.loadNpmTasks('grunt-contrib-cssmin');
    grunt.loadNpmTasks('grunt-replace');
    grunt.registerTask('default', [
        'uglify',
        'cssmin',
        'replace'
    ]);
};

In your markup instead of referencing the CSS-file normally instead write:

<style>
    @@defaultCss
</style>

Now everytime you run the default Grunt build command (by simply executing ‘grunt’ in your terminal) the placeholder variable ‘@@defaultCss’ in head.html will be replaced with the contents of your default.min.css file (in this case located in build/css/). The output file will be located in build/head.html. Note how I am here using the plugins cssmin and replace together. It’s therefore important to run cssmin BEFORE replace as it is cssmin that will generate the minified CSS output.

Server cache of static files

The Apache configuration

Make it so that static files (javascript, css and images) are cached for a longer duration so that they don’t have be fetched from the server on every new page request. This one is a bit trickier as it requires some server configuration (I am using Apache) and you may not have access to the server depending on what webhost you are using, if any. Regardless, this is my fix.

First you must enable the Apache mod “expires” by running:

a2enmod expires

Now restart your server for the change to take effect.

/etc/init.d/apache2 restart

Now that the expires mod is enabled I can configure my settings for the mod by adding it to my .htaccess file in my site root folder.

<IfModule mod_expires.c>
    ExpiresActive On
    ExpiresDefault "access plus 1 month"
    ExpiresByType image/jpg "access 1 year"
    ExpiresByType image/jpeg "access 1 year"
    ExpiresByType image/gif "access 1 year"
    ExpiresByType image/png "access 1 year"
    ExpiresByType text/css "access 1 month"
    ExpiresByType text/x-javascript "access 1 month"
</IfModule>

You can use whatever expiration time you like, learn more about this mod here.

Naturally there’s an obvious problem with this, what if we make changes to static files which we most certainly will if we regularly update the site? There’s a solution to this which once again involves Grunt.

Grunt job for version-tagging static files

Now I will install the Grunt plugins grunt-filerev, grunt-filerev-replace, grunt-contrib-clean and grunt-contrib-copy. Run:

npm install grunt-filerev grunt-filerev-replace grunt-contrib-copy grunt-contrib-clean --save-dev

Now modify your Gruntfile a bit like this:

module.exports = function(grunt) {
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        uglify: {
            options: {
                banner: '/*! My compressed and uglified JS | <%= grunt.template.today("yyyy-mm-dd") %> */\n'
            },
            build: {
                files: [{
                    src: 'js/**/*.js',
                    dest: 'build/main.min.js'
                }]
            }
        },
        cssmin: {
            minify: {
                src: 'css/default.css',
                dest: 'build/default.min.css'
            }
        },
        replace: {
            inline: {
                options: {
                    patterns: [{
                        match: 'defaultCss',
                        replacement: '<%= grunt.file.read("build/default.min.css") %>'
                    }]
                },
                files: [{
                    src: ['build/head.html'],
                    dest: 'build/head.html'
                }]
            }
        },
        filerev: {
            options: {
                algorithm: 'md5',
                length: 8
            },
            images: {
                src: 'build/**/*.{jpg,jpeg,gif,png}'
            },
            js: {
                src: ['build/main.min.js']
            }
        },
        filerev_replace: {
            options: {
                assets_root: './'
            },
            compiled_assets: {
                src: [
                    'build/main.min.js',
                    'build/default.min.css',
                    'build/head.html'
                ]
            }
        },
        copy: {
            main: {
                files: [{
                    src: 'img/*',
                    dest: 'build/',
                    expand: true,
                    flatten: true
                }, {
                    src: 'head.html',
                    dest: 'build/head.html'
                }]
            },
        },
        clean: {
            files: ['build/*']
        }
    });

    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.loadNpmTasks('grunt-contrib-cssmin');
    grunt.loadNpmTasks('grunt-replace');
    grunt.loadNpmTasks('grunt-contrib-copy');
    grunt.loadNpmTasks('grunt-filerev');
    grunt.loadNpmTasks('grunt-filerev-replace');
    grunt.loadNpmTasks('grunt-contrib-clean');

    grunt.registerTask('default', [
        'clean',
        'copy',
        'uglify',
        'filerev',
        'cssmin',
        'filerev_replace',
        'replace'
    ]);
};

The grunt plugin “filerev” will generate new files from the files specified, in this scenario image files and our js-file (main.min.js), however an MD5 hash will be generated based on the content of the file and appended to the file name. For instance a file named background.png would become background.86182fa8.png. If the files are changed the generated hash that is appended to the filename will also change. This means that everytime a file, for instance the compiled js-file is changed, it will no longer be cached on the server since the filename is changed and thus interpreted as a new file. By using this we can safely cache static files on the server.

The plugin “filerev_replace” will go through the specified files (main.min.js and default.min.css) and replace all references to the original files (like background.png) and change it to the newly generated hashed files (like background.86182fa8.png).

I also added the plugins clean and copy for cleaning previously generated build files and also copying files to be hashed to the build-folder. Here is a full explanation of the flow of tasks:

  • clean: Remove all previously built files from the build-folder before doing anything else.
  • copy: Copy all images from the img-folder to the build-folder. The reason is that we will later use filerev for the images in the build folder to generate hashed version but we want to let the original images remain. It is the hashed files in the build-folder that will be used, so references to images in css- and js-files should reference for instance build/background.png. The filerev_replace will then modify these references to build/background.86182fa8.png. We also copy the head.html file (this is a file that contains all tags in the head-tag, this file can then be included in the index.html file).
  • uglify: Minify, uglify and compress all js files from the js-folder and compile it into one js-file, build/main.min.js.
  • filerev: Generate hashed versions of all the images in the build folder and our js-file, build/main.min.js.
  • cssmin: Compress and minify css, output will be located in build/default.min.css.
  • filerev_replace:: Replace all references to images in main.min.js and default.min.css. As you can see build/head.html is also included, this is because it includes the reference to main.min.js which will be hashed.
  • replace: Replace the @@defaultCss placeholder variable in build/head.html and replace it with the content of the default.min.css file. This will inline the css. This is the reason we didn’t include css in the filerev task, because the css is inlined in the markup in this step anyway, it will not be a separate file that the server has to request.

This is about the same Grunt configuration I use on my site and I’m pretty happy with it. I managed to improve the performance of the site a lot by setting this up.

Testing server caching

To test that the files specified in the Apache configuration are now cached properly you can download and install the plugin Live HTTP Headers for Chrome or the Firefox version. Start the plugin and enter your site, watch the server file requests in the Live HTTP Headers plugin and you should now be able to see that image-files and js-files now have an Expires-header and a Cache-Control-header.

Asynchronus js-files

The async-attribute is a new addition to HTML5 and it’s an easy solution to load external javascript-files asynchronously. Note that this is not supported by older browsers like (IE8). Here’s how it looks:

<script async src="build/main.min.js"></script>

The end!

I hope you enjoyed this tutorial. There are more things I could mention regarding website performance but I think I covered the essentials, perhaps one day I will write a part 2 to this tutorial. 🙂

Advertisement

Create a customized Javascript code documentation

Introduction

In this tutorial I will cover the basics of how you can generate a Javascript code documentation for your codebase using the Grunt-plugin grunt-jsdoc. It works pretty much like Java-doc. Comments are added to your code that covers information about classes and functions, jsdoc will then generate a full API documentation using the information from these comments that can be navigated through in your browser. I will also cover how you can customize the default template of this JsDoc-webpage to match your desired look. In this tutorial I will use Grunt as my build tool. Also the site I am generating JsDoc for is built using Angular, but obviously you could still use this tutorial for any Javascript codebase. To see an example how this may look please see the JsDoc I generated for my own portfolio website.

Setup

First of all let’s add jsdoc to our dependency list in package.json.

{
    "name": "myPage",
    "version": "1.0.0",
    "repository": {
        "type": "git",
        "url": "https://github.com/me/myRepo"
    },
    "license": "MIT",
    "devDependencies": {
        "angular": "1.3.15",
        "grunt": "^0.4.5",
        "load-grunt-config": "^0.17.2",
        "load-grunt-tasks": "^3.3.0",
        "grunt-jsdoc": "1.1.0"
    }
}

Next add jsdoc as a Grunt build job to your Gruntfile.js.

module.exports = function(grunt) {
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        jsdoc: {
            dist: {
                src: [
                    'js/angular/**/*.js'
                ],
                options: {
                    destination: 'docs',
                    recurse: true
                }
            }
        }
    });
    grunt.loadNpmTasks('grunt-jsdoc');
    grunt.registerTask('build-docs', [
        'jsdoc'
    ]);
};

Install npm modules:

npm install

Now try running the newly created Gruntjob ‘build-docs’ just to see that you don’t get any errors. The output should be generated into the folder ./docs (as configured in options). The jsdoc should cover all Javascript code in the angular-folder, however since we haven’t added any jsdoc-comments yet the generated jsdoc site should be empty.

grunt build-docs

If you browse to localhost/docs in your browser you now see an empty jsdoc template. Now you are ready to start adding the jsdoc comments to your code.

Jsdoc global definitions

First of all we will ad a file for jsdoc global definitions, references we will use in the comments we add to our js-files. Create a js-file called jsdoc-global-definitions.js to your js-folder. Then add this file to your sources in the jsdoc Grunt task.

module.exports = function(grunt) {
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        jsdoc: {
            dist: {
                src: [
                    'js/angular/**/*.js',
                    'js/jsdoc-global-definitions.js'
                ],
                options: {
                    destination: 'docs',
                    recurse: true
                }
            }
        }
    });
    grunt.loadNpmTasks('grunt-jsdoc');
    grunt.registerTask('build-docs', [
        'jsdoc'
    ]);
};

In the file jsdoc-global-definitions.js I have the following configuration:

/**
 * AngularJS is a structural framework for dynamic web apps. It lets you use HTML as your template language and lets you extend HTML's syntax to express your application's components clearly and succinctly. Angular's data binding and dependency injection eliminate much of the code you would otherwise have to write. And it all happens within the browser, making it an ideal partner with any server technology.
 * @external "Angular"
 * @see {@link https://docs.angularjs.org/guide|Angular documentation}
 */

/**
 * @namespace controllers
 * @description Controllers linked to a specific view.
 */

/**
 * @namespace directives
 * @description Reusable directives that can be used by multiple views.
 */

/**
 * @namespace filters
 * @description Reusable filters that can be used by {@link controllers}, {@link directives} and {@link services}.
 */

/**
 * @namespace services
 * @description Reusable services that can be used by {@link controllers}, {@link directives} and {@link filters}.
 */

As you can see the file contains only comments written with the jsdoc syntax. Here we define global namespaces that we will later use when commenting the javascript codebase. Obviously you will have to modify your global definition file to fit the design pattern and frontend framework you have chosen (if any). Like I said, in this tutorial I am assuming that that the website is Angular-based, which is why I have defined the following namespaces: controllers, directives, filters and services. Each namespace also have a fitting description. We also have one external library dependecy, namely Angular, which is why I also added the first comment that has a link to Angulars developer documentation. This will list Angular as an external library. Also note that in the description you can link to other namespaces by writing {@link namespace}. To read more about jsdoc links please read this.

Adding jsdoc comments to your code

Here is an example of jsdoc comments written in jsdoc syntax added to an Angular controller:

(function() {
    var app = angular.module('myPage');
    /**
     * @constructor MyController
     * @memberof controllers
     * @description Controller for my webpage
     * @param {$scope} $scope - See {@link https://code.angularjs.org/1.2.26/docs/api/ng/type/$rootScope.Scope}
     * @param {services.myService} myService - Services used for cool stuff
     */
    var MyController = ['$scope', 'myService', function($scope, myService) {
        /**
         * @function controllers.MyController#getFibonacciNumberByIndex
         * @description Gets a number in the Fibonacci sequence by index
         * @param {number} The index of the Fibonacci sequence
         * @returns {number} The calculated Fibonacci number
         */
        function getFibonacciNumberByIndex(index) {
            var list = [1, 1];
            for (var i = 0; i <= index; i++) {
                var num1 = list[list.length - 1];
                var num2 = list[list.length - 2];
                var next = num1 + num2;
                list.push(next);
            }
            return list[index];
        }
    }];
    app.controller('MyController', MyController);
}());

As you can see in the controller MyController the function MyController is treated as the constructor, therefore the @constructor comment. I have also added the comment @memberof controllers which is referring to the namespace “controllers” that we defined earlier. MyController has two dependencies, $scope and myService, therefore these are documented as @param. Since $scope is a built-in Angular module I add a link to the documentation of $scope using the {@link} syntax. The other dependency is to to an Angular service in OUR codebase, therefore it is referenced to as services.myService (services is the namespace), this will generate a link to the documenation of myService.

The controller contains one function that returns a Fibonacci number by give index. For this function we define it using @function controllers.MyController#getFibonacciNumberByIndex. The syntax goes: namespace.fileName#functionName. The function takes in one parameter of the expected type {number} and returns a response also of the type {number}. To learn more about other parameter/return types read this.

Now generate your jsdoc by yet again running

grunt build-docs

and then check the generated jsdoc in localhost/docs. You should now see a menu to the right with external sources (Angular), Internal sources (MyService) and defined namespaces (controllers, directives, filters and services). If you click on MyController you should be able to see detailed description of the constructor and the function getFibonacciNumberByIndex.

Custom startpage

Right now the startpage of your jsdoc documentation should be blank, but you can add a custom template by creating a file named jsdoc.md and add a reference to it in your Grunt task:

module.exports = function(grunt) {
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        jsdoc: {
            dist: {
                src: [
                    'js/angular/**/*.js',
                    'js/jsdoc-global-definitions.js',
                    'js/jsdoc.md'
                ],
                options: {
                    destination: 'docs',
                    recurse: true
                }
            }
        }
    });
    grunt.loadNpmTasks('grunt-jsdoc');
    grunt.registerTask('build-docs', [
        'jsdoc'
    ]);
};

The jsdoc.md file should be written using Markdown syntax, same syntax used for Github readme-files. To learn more about it read here.

For an example, see how my file looks.

Custom stylings

If you see my jsdoc for my portfolio website you’ll see that it is a bit more colorful than the regular grey template. If you also wan’t your jsdoc page to have a more fabolous look follow these steps.

After you installed the grunt-jsdoc module using npm you should then be able to find the following folder: /node_modules/jsdoc/templates/default.

Copy this folder to your repository root folder:

sudo cp -rf /myRepo/node_modules/jsdoc/templates/default /myRepo/myTemplate

Now add the following setting to your Gruntfile.js:

module.exports = function(grunt) {
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        jsdoc: {
            dist: {
                src: [
                    'js/angular/**/*.js',
                    'js/jsdoc-global-definitions.js',
                    'js/jsdoc.md'
                ],
                options: {
                    destination: 'docs',
                    recurse: true,
                    template: './myTemplate'
                }
            }
        }
    });
    grunt.loadNpmTasks('grunt-jsdoc');
    grunt.registerTask('build-docs', [
        'jsdoc'
    ]);
};

Jsdoc will now instead of the default template in node_modules use template found in the myTemplate directory. Now you can modify this template as much as you want, this template folder includes css, scripts and markup that can be modified as you please.

That’s it! I hope you enjoyed this tutorial 🙂 🙂