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.
- 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
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>
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.