Sunday, May 16, 2010

Bundling Javascripts & CSS Files

If you are using jQuery for your site, it is possible that eventually your page header section may look like this:

 
<link href="stylesheets/ui.all.css" rel="stylesheet" type="text/css" />
<link href="stylesheets/admin.css" rel="stylesheet" type="text/css" />
<link href="stylesheets/maincontent.css" rel="stylesheet" type="text/css" />

<script src="scripts/jquery-latest.min.js" type="text/javascript"></script>
<script src="scripts/ui.core.js" type="text/javascript"></script>
<script src="scripts/mycustom.js" type="text/javascript"></script>
Now in those lines of code, there are 6 requests being made separately to the server and this can become a bottle neck for your site performance. Both YSlow and PageSpeed (read here and here) recommend reducing the number of requests being made as a way to create a high performance site.

Easy, right? Just combine the files with a simple text editor - both javascript files and css files are plain text anyway! Yes - you can do that. But this approach will yield a nightmare-ish maintainability and just plain ugly.

Justin Etheredge made a small framework to precisely do this - he called it Bundler.Framework. You can read his latest blog post about it here and download the code here. It is very simple to use and I have had excellent results with it. The framework itself is pretty self-explanatory and Justin's posts highlight on how to use it pretty well.

Through this post, I just want to highlight some of the small things I have to do to eventually get it working (and it has been working awesomely).
  • Add a refence to Bundler.Framework.dll
  • For the code to actually create a single js file (or css), you will need to make sure that your site's compilation is configure to NOT debug. You can do this in web.config, or else your js files will just be listed on one by one instead of being combined:
    <compilation debug="false" targetFramework="4.0">
    
  • Bundler also provide options to compress your css and also to minify your js with different minifiers.
     
    <%
        Response.Write(
            Bundler.Framework.Bundle.Css()
                .Add("~/stylesheets/ui.all.css")
                .Add("~/stylesheets/admin.css")
                .Add("~/stylesheets/maincontent.css")
                .WithCompressor(Bundler.Framework.Css.Compressors.CssCompressors.YuiCompressor)
                .RenderOnlyIfOutputFileMissing()
                .Render("/JJCMS/Content/Stylesheets/combined.css")
        ); 
    %>
    <compilation debug="false" targetFramework="4.0">
    
  • Bundler also provide options to only render combined js or css file if the rendered file is non-existent. You use the "RenderOnlyIfOutputFileMissing" option. See code above for example.

So now, with Bundler, your js and css will be rendered like this:
 
<script type="text/javascript" src="/scripts/combined.js?r=67EFAB2205D48FBBB390A6F11C8A4002E"></script>
<link rel="stylesheet" type="text/css" href="/stylesheets/combined.css?r=A84FC8E0836CD939A781066B0BBDE028" />

2 comments:

tommy.vu said...

So does that integer string after the .js? and .css? stay the same? Or does it change every time a page renders? I'm wondering what the browser can actually cache vs. needing to be reloaded every time.

Johannes Setiabudi said...

Browser will cache it. The number (GUID) will only change if there is a new version of the file - in which the browser then will get the newest version and replace the old cache.