One of the features of Visual Studio 11 that I’m really looking forward to taking advantage of is CSS and JavaScript minification and bundling.
As developers, we like to be able to break our applications into reusable components. That makes development and maintenance much more manageable. Sometimes, however, it can have an impact on performance. The more CSS style sheets and JavaScript files you add to a page, the longer that page will take to download – and it’s not just a matter of the size of the files; the number itself is a problem. Http requests are expensive; the more you have, the slower your page is to load, even if the absolute size of the downloaded files is not very large.
Visual Studio 11 comes with two new features that make it more practical to break our JavaScript and CSS into multiple file. Bundling and minification give us a means of ‘componentizing’ our Web applications, without taking the performance hit of too many http requests. Bundling allows you to combine multiple files into a single larger file. Minification removes unnecessary white spaces to ensure that the new file is as small as possible. Taken together, the two techniques have a major impact on performance.
There are two versions of the functionality: the simple, automatic way; and the much more useful custom approach. In the simple version, the contents of folders are automatically bundled and minified if your <link> and <script> tags to point at the default directories and don’t specify individual files, thus:
<link href=”/Content/css” rel=”stylesheet” type=”text/css” />
That gets converted into html along these lines at runtime:
<link rel=”stylesheet” type=”text/css” href=“/Content/themes/base/css?v= UM624qf1uFt8dYtiIV9PCmYhsyeewBIwY4Ob0i8OdW81″ />
That’s fine as far as it goes, but is also rather limiting. You have to use default locations, and there’s no scope for having different bundles for different versions of your application.
The MVC4 template comes with a feature that’s designed to support this – ResolveBundleUrl():
<link href=”@System.Web.Optimization.BundleTable.Bundles.ResolveBundleUrl (“~/Content/css”)“ rel=”stylesheet” type=”text/css” />
Now we can define our own custom bundles. In my case, I can create two new sub-folders under the style and script paths, one for mobiles and one for the standard Web site:
Then I change the paths passed to ResolveBundleUrl() to point at my new folders….
<link href=”@System.Web.Optimization.BundleTable.Bundles.ResolveBundleUrl(“~/Content/site/css”)“ rel=”stylesheet” type=”text/css” />
<script src=”@System.Web.Optimization.BundleTable.Bundles.ResolveBundleUrl(“~/Scripts/site/js”)“></script>
and… it doesn’t work.
Although ResolveBundleUrl() supports adding non-default paths, you have to do a little more work and register your new paths in the application start event in the Global.asax before it can use those paths
This tells the system that we’re adding two bundles – one for .css files and one for .js, and that all the files in the specified directories should be part of the bundle. (You can also specify individual files if you need to control the loading order beyond the default settings, which first prioritize known libraries like jQuery, then load the files alphabetically).
One nice additional feature is that the bundles are cached on the browser using the generated path (e.g. css?v=UM624qf1uFt8dYtiIV9PCmYhsyeewBIwY4Ob0i8OdW81). This means that not only are they loaded faster the first time the user visits the site, they do not need to be downloaded again the next time. More significantly, if any of the files in the bundled folder changes, so does the generated path. That means your users will never be stuck with old versions of your style sheets or JavaScript files.