Monday, March 5, 2012

Sprite and Image Optimization Framework and .NET Minifier Bundling

[NOTE: This code doesn't fully work (with the version of bundler in Visual Studio 11 Beta, newer versions may address this) s the bundler combines the files but leaves the relative path urls  in the css unchanged. As they no longer point to the correct paths they break. If the images are base 64 encoded in the css it will work however. So, as I don't want to right my own parser, SquishIt remains my choice as it will create the bundles with the correct path to the root.]

I use the Sprite and Image Optimization Framework for spriting images. The SIOF is currently on preview 4, but hopefully still going forwards.

Previously I have been using SquishIt for bundling and minification and it has worked really well for me. But, with Bundling and Minification built in to Visual Studio 11, I thought I would see what it took to get it working with the SIOF.

Specifically, the SIOF generates a separate css file on the fly for each subfolder inside an "App_Sprites" directory (by convention, but can be configured otherwise of course). Also, it generates two different versions of the css, a 'lowCompat.css' for older browsers and a 'highCompat.css' for modern browsers.

In my case, I like to separate my sprite folders for clarity and for possible inclusion in specific locations and so end up with a lot of css files. I don't want the overhead of making separate http calls to load each css file individually, so I need to bundle them together.

So this is what I needed to do to get bundling working. Note, this approach does not provide minification as there are problems with how the SIOF spits out css when it is minified.

In the global.asax.cs file, in Application_Start, I had to create custom bundles, which look like this:


    BundleTable.Bundles.EnableDefaultBundles();

    var highCompat = new Bundle("~/spritehighbundle", new NoTransform());
    var lowCompat = new Bundle("~/spritelowbundle", new NoTransform());

    highCompat.AddDirectory("~/App_Sprites","highCompat.css",true);
    lowCompat.AddDirectory("~/App_Sprites","lowCompat.css",true);

    BundleTable.Bundles.Add(highCompat);
    BundleTable.Bundles.Add(lowCompat);

The first line is used for the regular bundles you might create and isn't part of the custom bundles I'm creating here, but I left it in as I use it in other places.

First, I create a new bundle and tell it where to create the file and give it a transform.


    var highCompat = new Bundle("~/spritehighbundle", new NoTransform());


Because of the way the SIOF creates the css, if you attempt to minify the file it will generate bad css, so I set it to "new NoTransform()". I had the same issue with SquishIt and used a similar approach to deal with the issue.

If you want to try minifying you could use "new CssMinify()" instead and that might actually work for the lowCompat.css file, but I didn't check.

Then, I point it to the directory I want to bundle, "~/App_Sprites".

    highCompat.AddDirectory("~/App_Sprites","highCompat.css",true);

By default it will take all the subfolders under that, though you can configure them individually. I give it a string match, in this case either "highCompat.css" or "lowCompat.css" so I can get two different bundled css files depending on the users browser capabilities.

Then, add to the BundleTable.Bundles:


    BundleTable.Bundles.Add(highCompat);

And we are done in the global. In the head of my html page (or layout.master):


    @if(ImageOptimizations.LinkCompatibleCssFile(Request.Browser) == "highCompat.css") {
        <link href="@BundleTable.Bundles.ResolveBundleUrl(" rel="stylesheet" spritehighbundle")"="" type="text/css" ~=""></link>
    } else {
         <link href="@BundleTable.Bundles.ResolveBundleUrl(" rel="stylesheet" spritelowbundle")"="" type="text/css" ~=""></link>
    }

The first line checks to see which version of the css your browser should see. Inside the if statement it points you to the bundle you configured in the Application_Start.

The output in the html looks like:


    <link href="/spritehighbundle?v=Pz1dYVdxXX3byT-0-Sd3NUQdrbaEEsozXT6mYfGVn5o1" rel="stylesheet" type="text/css"></link>


With a version hash that will invalidate the css if you make a change.

And that is it. Not necessarily painless and not necessarily better than how I had it set up under SquishIt, but working at least.
Labels: sprite and image optimization framework bundling minification

1 comment: