Cassette. The Javascript CSS Minifying Combining Compressing Tool for ASP.NET
In a nutshell, Cassette will minify, combine, compress and cache your css and javascript files, reducing your page load times. It does a heap more too like LESS, Coffeescript, file ordering, debug vs release files, html templates, CDN assets and more.
It’s easy to get started with too. No more complicated than referencing your css and javascript files directly.
No Excuses
I hadn’t implemented any minifying, combining, etc for my app Pik Pak. As I saw it (with my lean goggles on), it worked fine, it was good enough. I could benefit from it but really was the effort worth it. I’d bookmarked Cassette ages ago when it was Knapsack and made it a someday-maybe task. As I was giving Pik Pak a facelift, removing a garish design and replacing with the wonderful Twitter Bootstrap, it was time to bring it in.
My opinion has changed, it is worth it. It takes so little effort. I know in my next asp.net project it will go in at the beginning, it’s so easy to use there’s no excuse not too (Except perhaps 1, if you’re not using .NET 4.0). In dev I get the scripts unaltered, just like I’d inserted them manually and in prod they get minified, compressed and combined.
How it Works (Briefly)
I want to share with you a little bit about how it works, as it wasn’t clear to me at first. I originally mistook the Bundle.Reference() calls to be informing Cassette about the asset and saying it’s required on this page. There’s actually 3 parts to Cassette, in it’s simplest form (there’s tons more to it):
There are 3 parts to getting your scripts/css combined, minified etc
1. Tell Cassette about your css/js files
Cassette doesn’t automatically discover your files (assets), you need to tell it where they are and how they should be combined (bundles). A bundle is a number of files that will be combined into one minified/compressed file (in production) that is referenced by one script/stylesheet tag, thus causing a single download. The default CasseteConfiguration.css that get’s dropped in by nuget creates a bundle for every css or javascript file. Which is better than nothing. You’re getting minification, compression, caching, versioning but it’s still a download per file, there’s no combining. So you want to define bundles to minimise the number of downloads the browser has to make.
As well as defining what goes into a bundle you can also define the order within bundle and dependencies between bundles or files. Eg your script might depend on a framework like jQuery or Backbone.
There’s a number of ways to define what goes into a bundle.
- A bundle per file (default config).
bundles.AddPerIndividualFile<StylesheetBundle>("Content/Stylesheets");
- A folder as a bundle.
bundles.Add<StylesheetBundle>("Content/Stylesheets");
- A folder as a bundle and each direct subfolder as a bundle to.
bundles.AddPerSubDirectory<`ScriptBundle`>("Scripts");
All of these kind be fine tuned to.
Defining the order they get rendered and dependencies (If your script depends on jquery and you reference your file or bundle in a page, jquery will automatically be pulled in) can be:
- Embedded in the file itself.
// @reference ~/lib/backbone.js
or/// <reference path="~/lib/backbone.js" />
- or in bundle.txt files in the folders that are being bundled.
If you’re still with me! Defining the bundles is the most difficult part but once you’re done the next 2 are straight forward.
2. Request scripts to be added to the page
This is were inform Casssette, that you need xyx file on this page. This can be in your master page/Razor layout, page or partial view. You can either
- reference the file itself (and the containing bundle will be inserted)
@Bundles.Reference("lib/backbone/backbone.marionette.js");
- or you can reference the bundle
@Bundles.Reference("lib/backbone")
You can also optionally specify where the script should be rendered. @Bundles.Reference("lib/modernizr.js", "header")
3. Render the scripts
Call into Cassette to render the script/css tags at that point. Normally this will be one or 2 lines in your layout/masterpage file.
<!DOCTYPE html>
<html>
<head>
<title>Web App</title>
@Bundles.RenderStylesheets()
</head>
<body>
... @Bundles.RenderScripts()
</body>
</html>
There’s so much more, all in the documentation.
Tips
So far, these are my thoughts on how to get the best out of Cassette with least effort.
Render scripts before closing body tags
Everyone knows you should render your scripts before the closing body tags, to prevent the page load being blocked. Have your main @Bundles.RenderScripts();
before the closing body tags and have a specific one @Bundles.RenderScripts("header")
in the header for those scripts that need it (Modernizr).
jQuery CDN
If your using jquery, you should create a bundle just for itself. Create a folder (probably called jquery), add the jquery file and a bundle.txt file containing the following:
[external]
url = //ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js
fallbackCondition = !window.jQuery
[assets]
jquery.js</pre>
Then reference the bundle as normal @Bundles.Reference("scripts/jquery");
In dev you get your full jquery js file and in prod you get googles cdn jquery file (most likely already cached in the broswer) with a fallback to your minified/compressed one.
Convention
Use bundles.AddPerSubDirectory<ScriptBundle>("Scripts");
and arrange your scripts/css into folders for each bundle you want. That way you don’t have to go back and touch the CasetteConfiguration. You can leave it upto convention and fine tune with a bundle.txt file in the folders/bundles where needed.
It’d be great to hear how your using Cassette, in the comments and any tips that have helped.