Skip to content

Instantly share code, notes, and snippets.

@craigpalermo
Last active March 11, 2018 05:48
Show Gist options
  • Save craigpalermo/2e4e93e2e059cfaf7524 to your computer and use it in GitHub Desktop.
Save craigpalermo/2e4e93e2e059cfaf7524 to your computer and use it in GitHub Desktop.
Use Grunt tasks to consolidate HTML, CSS, and JS files to improve application performance

Optimizing route load times in AngularJS

Abstract

By consolidating HTML templates, CSS stylesheets, and JavaScript, we can greatly reduce the amount of time spent waiting for requests to retrieve external resources and make transitions between routes appear much smoother.

Advantages of consolidation

  • Eliminates lazy loading of most HTML templates and CSS
    • Faster transitions between templates when changing routes
    • Don't have to rely on loading external stylesheets or angular-route-styles to inject/remove styles when changing routes

Method

The following NPM packages can help us compile multiple Jade, Stylus, and JavaScript files into a single JavaScript file that the browser will load.

  • grunt-contrib-jade
  • grunt-contrib-stylus
  • grunt-contrib-uglify
  • grunt-angular-templates

Before beginning the build process, make sure that all of the templates for which you have external stylesheets are set up to use Jade's import feature to pull them inline when compiling the template. Place the style block at the top of the template file, above the content. This will ensure that the styles are loaded before the content is added to the DOM.

style
  // this is the relative path from the the current jade
  // template to the css file that you wish to include
  // inside the style block
  include ../../css/my_route_style.css

div
  // content goes here...

The following are example configurations for the necessary Grunt tasks.

Step 1: Compile Stylus -> CSS

This step is only necessary if you're using Stylus (or another CSS preprocessor, in which case you should use the appropriate Grunt plugin). If not, skip to step 2.

stylus: {
  compile: {
    files: [{
      expand: true,
      compress: false,
      cwd: "stylus/",
      src: ['**/*.styl'],
      dest: './build/css/',
      ext: '.css'
    }]
  }
}

Step 2: Compile templates from Jade -> HTML

Each of the resulting HTML partials will include style blocks containing the CSS rules for that template.

jade: {
  compile: {
    options: {
    pretty: false
  },
  files: [{
    expand: true,
    cwd: "jade/",
    src: ['**/partials/*.jade'],
    dest: './build/',
    ext: '.html'
  }]
}

Step 3: minify, combine, and cache HTML templates

This is where most of the magic happens. We'll use grunt-angular-templates to take all of the HTML templates we just created and put them into a single JS file that will load after our Angular app is defined. This JS file contains all of the templates and styles needed for our application's routes, and tells Angular to cache them using the $templateCache service. That means that when we visit a route, Angular will look for a cached template before attempting to load it from the remote resource.

The following task takes all the templates from partials/ and puts them into a single file, js/templates.js. I've also added the htmlmin option to reduce the size of the resulting file.

ngtemplates: {
  app: {
    src: 'partials/**.html',
    dest: 'js/templates.js',
    options: {
      htmlmin: {
        collapseBooleanAttributes: true,
        collapseWhitespace: true,
        removeAttributeQuotes: true,
        removeComments: true,
        removeEmptyAttributes: true,
        removeRedundantAttributes: true,
        removeScriptTypeAttributes: true,
        removeStyleLinkTypeAttributes: true
      }
    }
  }
}

Step 4: uglify and combine JS files

Chances are that your application is made up of multiple JS files. Uglify can combine those files into one, as well as minify it to save space.

The following task combines app.js, templates.js, and the rest of the JS files in js/ (in that order) into a single file, app.min.js.

/* minify compiled JavaScript */
uglify: {
  dist: {
    options: {
      mangle: false,
      preserveComments: false
    },
    files: {
      'js/app.min.js': [
        'js/app.js',
        'js/templates.js',
        'js/**/*.js'
      ]
    }
  }
}

** Step 5: load app.min.js into your index.html**

As with any Angular application, you need to import your JS files (er, file). Make sure you import it after AngularJS.

<!-- index.html -->
<head>
...
<script src="js/angular.min.js", type="text/javascript"></script>
<script src="js/app.min.js", type="text/javascript"></script>
...
</head>

Conclusion

Your application will no longer have to download external templates or stylesheets! If you watch the network tab of your browser's dev tools, you should see 304 status codes when switching between different routes. This means that the requested resource hasn't changed since it was last cached and your browser is using the cached version, which is what we wanted to happen. This reduces your application's bandwidth requirements, and improves loading times for templates and their corresponding stylesheets.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment