Minimizing initialization time in AngularJS
Preloading templates and RESTful resources
One of the most common complaints against client-side JavaScript frameworks is the slow initial load time. There’s definitely some truth in this, but probably not for the reason you’d expect.
Rendering the view with JavaScript is usually quite fast, it’s the extra HTTP requests which tend to slow things down.
To investigate, let’s work with a vanilla AngularJS app for viewing hikes and inspect its initialization:
Eeek. When it’s all said and done, we waited a whole second for only 13.5KB. Where did all the time go?
- Well, first we request the standard bootstrapped HTML.
- Next, we need the CSS and JavaScript (including Angular).
- Then the current page’s HTML template.
- And finally the RESTful resources.
This wouldn’t be bad, except that each of these steps is sequential. And sequential adds up. However, we can fix this with one key insight — we know which resources are required as soon as the user makes the initial request.
Preloading HTML templates
Rather than waiting to download the HTML template partial, let’s inject the partial into the initial request. To do so, we use some AngularJS magic called text/ng-template.
The script type text/ng-template allows us to preload Angular’s template cache with HTML, saving us an HTTP call during initialization.To use it, simply include the HTML in the script, along with the id of the partial:
<script type="text/ng-template" id="/partials/hike.html">
<h1>This is my template for {{hike.name}}</h1>
</script>
Now, when Angular goes to find /partials/hike.html, it will find the partial in its cache. And with that, we just saved a whole step during initialization:
Preloading non-HTML resources
While HTML templates can use the built-in text/ng-template mechanism to warm up Angular’s template cache, there is no built-in mechanism for preloading other resources, like JSON.
So we’ll build our own. First, we need a resource cache:
angular.module("myModule").
factory("resourceCache",["$cacheFactory",
function($cacheFactory) {
return $cacheFactory("resourceCache");
}
]);
Next we need a directive that will populate this cache with its contents, similar to the way text/ng-template works:
angular.module("myModule").
directive("preloadResource", ["resourceCache",
function(resourceCache) {
return { link: function (scope, element, attrs) {
resourceCache.put(attrs.preloadResource, element.html());
}};
}
]);
Then we have to deliver this resource on the initial request, and Angular will load it into the cache:
<div data-preload-resource="/api/v1/hikes/the-narrows.json">
{"name":"The Narrows"}
</div>
The last step is to use this cache when we make our normal AJAX call:
$http({
method: "GET",
url: "/api/v1/hikes/the-narrows.json",
cache: resourceCache });
And with that, we’ve now preloaded both the template and JSON, cutting initialization in half:
Why not preload everything?
You could imagine taking this logic even further, preloading all the CSS and JavaScript into a single request. While you’d save some requests, you’d lose something even more valuable — caching. Now the browser can’t cache these commonly requested resources.
The general rule is to only preload resources you were going to request anyway. With a cacheable resource, you may not need to make the request at all.
To see this approach in practice, check out hike.io or its github page.