When you are building AngularJS apps you will probably want to store all your various controllers, directives, filters, etc. into different files to keep it all nicely separated and easy to manage. However, putting script blocks to all those files in your HTML is not efficient in the least. Not only do you have several round-trips to the server, the browser will be downloading a lot of code that is designed to be readable and maintainable, potentially with lots of additional whitespace and comments.
If the back end of your application is using .NET then you can bundle together CSS and Javascript files to make them more optimised.
For example, I have a small AngularJS prototype application that uses bundling so that, when it is run with the optimisations turned on, it will need less files and more compact javascript and CSS. The method that creates these bundles looks like this:
public static void RegisterBundles(BundleCollection bundles) { bundles.Add(new StyleBundle("~/Content/base-styles.css") .Include("~/Content/bootstrap.css") .Include("~/Content/angular-ui.css") .Include("~/Content/angularCatalogue.css")); bundles.Add(new ScriptBundle("~/Scripts/base-frameworks.js") .Include("~/Scripts/jquery-{version}.js") .Include("~/Scripts/angular.js") .Include("~/Scripts/angular-resource.js") .Include("~/Scripts/angular-ui.js") .Include("~/Scripts/bootstrap.js")); bundles.Add(new ScriptBundle("~/Scripts/angular-catalogue.js") // Configure the Angular Application .Include("~/ngapp/app.js") // Filters .Include("~/ngapp/filters/idFilter.js") .Include("~/ngapp/filters/allBut.js") // The services .Include("~/ngapp/Services/colourService.js") .Include("~/ngapp/Services/brandService.js") .Include("~/ngapp/Services/productTypeService.js") .Include("~/ngapp/Services/productService.js") .Include("~/ngapp/Services/sizeService.js") // Directives .Include("~/ngapp/Directives/userFilter.js") .Include("~/ngapp/Directives/productDetailsDirective.js") // Controllers .Include("~/ngapp/Controllers/productSearchController.js") .Include("~/ngapp/Controllers/productDetailController.js") .Include("~/ngapp/Controllers/editProductController.js")); }
This method is called from the Application_Start()
method in global.asax.cs.
What this does is set up a number of bundles. In this case three bundles are set up. One for the CSS, and two for javascript (one is a set of standard third party libraries, the other is the angularJS application itself).
In the layout or view you can then reference these bundles using the path passed in to the constructor. Like this:
<html> <head> <!-- Other bits go here --> @Styles.Render("~/Content/base-styles.css") </head> <body> @RenderBody() @Scripts.Render("~/Scripts/base-frameworks.js") @Scripts.Render("~/Scripts/angular-catalogue.js") </body> </html>
Remember to use the tilde notation just like in the code that defines the bundles.
When the optimisations are turned off the scripts will render as a script block per include. When the optimisations are turned on then it outputs one script block. When the server receives a request for that script it resolves the name to match the bundle, it then sends back an amalgamated and minified version of that script file. This then loads much faster on the client as there are less roundtrips to the server and takes much less bandwidth.
Here’s what the two scenarios look like:
Optimisations turned off
This is what the two @Script.Render()
blocks at the end of the HTML look like:
<script src="/Scripts/jquery-1.9.1.js"></script> <script src="/Scripts/angular.js"></script> <script src="/Scripts/angular-resource.js"></script> <script src="/Scripts/angular-ui.js"></script> <script src="/Scripts/bootstrap.js"></script> <script src="/ngapp/app.js"></script> <script src="/ngapp/filters/idFilter.js"></script> <script src="/ngapp/filters/allBut.js"></script> <script src="/ngapp/Services/colourService.js"></script> <script src="/ngapp/Services/brandService.js"></script> <script src="/ngapp/Services/productTypeService.js"></script> <script src="/ngapp/Services/productService.js"></script> <script src="/ngapp/Services/sizeService.js"></script> <script src="/ngapp/Directives/userFilter.js"></script> <script src="/ngapp/Directives/productDetailsDirective.js"></script> <script src="/ngapp/Controllers/productSearchController.js"></script> <script src="/ngapp/Controllers/productDetailController.js"></script> <script src="/ngapp/Controllers/editProductController.js"></script>
And when this is rendered in the browser, the following calls are made.
There are 18 requests in the above example. 901kb is transferred to the browser and it took 911ms to complete loading everything (the above does not show images, css or ajax calls that are also downloaded as part of the page)
Optimisations turned on
Now, compare the above to this representation of the same section of the page:
<script src="/Scripts/base-frameworks.js?v=oHeDdLNj8HfLhlxvF-JO29sOQaQAldq0rEKGzugpqe01"></script> <script src="/Scripts/angular-catalogue.js?v=fF1y8sFMbNn8d7ARr-ft_HBP_vPDpBfWVNTMCseNPC81"></script>
And when rendered in the browser, it makes the following requests:
There are now just two requests, one for the base-framework bundle, and one for the angular-catalogue (our application code) bundle.
Because the bundling process minifies the files the amount of data transferred is much smaller too, in this case 223kb (a saving of 678kb or roughly 75%). For established frameworks that ship with a *.min.js version the bundling framework will use that convention and use the existing minified file. If it can’t find one it will minify the file for you.
And because there is less data to transfer and less network round-trips to wait for the time to fully load the page has been reduced to 618ms (a saving of 293ms or roughly ⅔ of the time of the previous request).
More information
There is a lot more to bundling than I’ve mentioned here. For a more in depth view of bundling read Scott Guthrie’s blog on Bundling and Minification Support.