Web Optimization in Visual Studio 2012 RC

The Web optimization framework has gone through some major changes between the Visual Studio  11 Beta and Release Candidate releases. The changes coalesce around 2 primary goals:

  1. Enable greater control over the default bundles that are registered with the Web application templates.
  1. Support different debug and release modes so that resources will not be bundled while developing and debugging, but will be bundled and minified when the application is deployed into production.

Specific Changes

In order to achieve these goals, several changes were introduced in both the way that bundles are declared and registered and in the way that bundles are referenced from within a view.

Changes in bundle registration and declaration

EnableDefaultBundles has been deprecated and will be removed for RTM.

When you create a new project, one of the first changes that you may notice is the addition of a .cs or .vb file called BundleConfig. This file has a single method called RegisterBundles and is called from global.asax’s Application_Start method. This class method now performs all of the creation and registration logic for bundles that are used by the default templates. In beta, we performed all of this registration by calling a method which was built into the framework assembly such as RegisterTemplateBundles or EnableDefaultBundles. Having this method included as code in your project now gives you greater visibility into what bundles are available to your views as well as gives you more flexibility in modifying the configuration. In RC, we also create separate bundles for jQuery, jQuery UI, etc. in the default bundle configuration to enable more fine-grained control over what assets are included on a page and where they are included.

Additionally, we’ve simplified the code that declares and registers the bundles. For example, consider the following Beta registration:

In RC, this code is now written as:

As you can see, some of the improvements here include:

  • Creating strongly-typed bundle classes for styles and scripts which automatically wire up the appropriate default bundle transformer (minifier)
  • Enabling method chaining to avoid the 3-step process of create/configure/add to collection
  • Enable file inclusion via a params array to avoid multiple calls to AddXxX

Changes in how bundles are referenced within views

In addition to streamlining the workflow for declaring bundles and registering them with the bundle manager, we’ve also improved how bundles are included in views. These changes will greatly simply the task of reconfiguring how bundling and minification works when switching between different development configurations such as debug and release.

In beta, a bundle was referenced in a view by using a helper method to generate the appropriate bundle URL inside of a script tag, as follows:

<link href=’@BundleTable.Bundles.ResolveBundleUrl(“~/Content/themes/base/css”)’ rel=”stylesheet” type=”text/css” />

<script src=’@BundleTable.Bundles.ResolveBundleUrl(“~/Scripts/js”)’></script>

While this approach works fine, for resolving the bundle URL – even for getting a version-stamped bundle URL – it becomes limiting when working in a debugging scenario. In this case, it is likely that the bundle should be expanded into a set of assets, and that each one of those should render an individual script or link element on the page.

As such, for RC, this behavior can be achieved with the Styles.Render and Scripts.Render helper methods. For example, the code above can be rewritten as:

@Styles.Render(“~/Content/themes/base/css”)

@Scripts.Render(“~/Scripts/js”)

Additionally, these helper methods accept a params array of URLs to render, so you can pass references to several bundles in the same helper method call:

@Styles.Render(“~/Content/themes/base/css”, “~/Content/css”)

Using this approach, when in debug mode ((<compilation debug=”true” />), script and styles that are registered into bundles will be expanded into individual script or CSS elements on the page. When releasing the site, those files will be bundled and minified. By default, this property looks at the IsDebuggingEnabled property of the current HttpContext, meaning that it can be controlled in Web.config by setting the debug attribute in the compilation Element to false as shown below:

<compilation debug=”false” />

You can override the Web.config setting with the static EnableOptimizations property on the BundleTable class.

While these helper methods provide support for switching between debug and release modes, there may be times where you still want more direct control over your HTML markup and retain the previous behavior or having the optimization framework simply generate the correct bundle URL. In RC, we continue to make that scenario possible through the URL helper method on the Styles and Scripts classes, as follows:

<script type=”text/javascript” src=’@Scripts.Url(“~/bundles/modernizr”)’></script>

This approach provides complete control over the script or link markup elements, though at the expense of debug support.

Plugging in custom minification libraries

As in previous release, the framework continues to enable custom transformation libraries to participate in rendering bundles, in addition to the default JsMinify and CssMinify transformation classes. In RC, however, the framework enables multiple transformations to be applied to a single bundle (whereas in Beta, this was accomplished by creating a custom composite transform class). For example, consider the following custom bundle transform class that converts LESS script into CSS using the dotless library.

This transformation can be chained together with the default CssMinify bundle transform and configured for a bundle using the following:

The order of transform execution is based on the order the transform instances are added to the bundle’s Transforms collection. In the code above, LessTransform  is run before CssMinify.

About Howard Dierking

I like technology...a lot...
This entry was posted in Uncategorized. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Paul DiPalma

    How does the “version-stamped URL” work? How does it change when it’s deployed? Can it be overrriden? I’m having issues where the version stamp doesn’t change. Other than that it works great!

  • Anonymous

    fixed – auto-correct bit me – thanks for the catch!

  • Anonymous

    fyi the word you meant to use is ‘deprecated’ not ‘depreciated’

  • ARJindal

    UseCdn seesm to be a property of BundleCollection and not of individual bundle.

  • Pat Lupiani

    So what happens when we have an existing site? We don’t get the shiny new config file.

  • Anonymous

    In your views, are you using the Scripts.Render or Styles.Render
    helper methods and referencing your bundle with a virtual path “~/..” [1]?

    [1] http://stackoverflow.com/questions/11952491/asp-net-mvc-4-app-with-bundling-and-minification-why-is-minification-enabled-in

  • DotnetShadow

    Hi,
    In ASP.NET MVC 3 using Microsoft ASP.NET Web Optimization Framework 1.0.0 from NuGet and setting debug=”true” in web.config causes not scripts to be outputted at all which is a bug

  • Anonymous

    not in the current version, though this is something on our list for the future – though that was why I asked the question about content-rewriting, since it is directly related to this capability

  • Anonymous

    That makes sense – we’ve been exploring a less-often used feature of Razor that would give you the best of both worlds in the sense of being able to templatize the markup.  However, it was limited to Razor and even then, the syntax, while interesting and terse, was not exactly easy to follow.  We’re still investigating other options, but also open to suggestion.

  • http://twitter.com/kurtschindler Kurt Schindler

    I’m guessing Donatas is wishing that Styles.Render() could be configured to output something like which is exactly what I’m trying to do at the moment. It would definitely be nice if we could override the entire html tag that is rendered so arbitrary attributes could be added.

    In release mode this isn’t a big deal, I can just write my own tag and use Styles.Url() to specify the href.  But in debug mode this isn’t an option and I end up having to place an ‘if debug’ statement and enumerate the files by hand.

  • Matthew Kimber

    Is there any way in the current style transform to resolve relative paths for resources specified with the “url()” reference in CSS?

    I know in the Bundle Transformer project (http://bundletransformer.codeplex.com/) the author has already implemented this, but it seems like this would be a feature in the default transform from MS.

  • Mike

    Oh, and another thing: when using @Scripts.Render and using an url that is not registered as a bundle, also don’t render the script tag, as it will 404.

  • Mike

    So far this is working really well. But I did notice that a script tag is rendered by @Scripts.Render(), even if no scripts are included in the referenced bundle. This can be further optimized, so the browser doesn’t do a request if there’s going to be nothing there.

  • Jay Querido

    This is something that would be huge for me. I’ve got a Web API project, and a “CDN” project that holds my index.html, tmpls, js, images, and css. It’s a Backbone app with several libraries and tonnes of modules. I’d love to be able to easily bundle them up in Team City and reference them in my index.html file. Then all of this could actually live nicely on the CDN with automatic deployment.

  • http://www.facebook.com/profile.php?id=527758249 Doug Wilson

    How can we make the budling work with WebForms?  Particularly the versioned URL for getting it to refresh the script after changes?

  • http://dmytroduk.com/ Dmytro Duk

    What about inline scripts budnling, is it going to be supported?

  • olivier

    Merci. Great Post

  • Anonymous

    our assembly shouldn’t be in the GAC – it’s bin deployed via NuGet.  Can you check that 1) you have the Microsoft ASP.NET Web Optimization Framework 1.0.0-beta2 and WebGrease 1.0.0 packages installed in the project and that b) you have the System.Web.Optimization and WebGrease assemblies copied into your bin folder?

    thanks,

    _howard

  • clduvall

    Installed VS2012 RC here and out of the box getting an error about Optimization not being in System.Web.    It doesn’t appear that that DLL is being loaded in the GAC, although I’m not sure it needs to be.  Any ideas on how to handle this?  Just running the default project for WebAPI compiles but won’t execute…

  • Linda Albert

      In fact, I already have a working prototype app that consumes the feed and displays it based on the  application template. Application Templates

  • Pingback: MVC 4 @Scripts "does not exist"

  • Andrey Taritsyn

    Demis, i recommend you try a modular extension for Microsoft ASP.NET Web Optimization Framework
    – Bundle Transformer (http://bundletransformer.codeplex.com/)

  • Anonymous

    “dependency-free, extremely fast, pure node.js-based solution”

    doesn’t that mean that you’re taking a dependency on Node?

    Wrt to the text file config + no dependencies, we actually have that today, though it’s not a first class “feature” yet – and by not a first class feature, I mean there’s no documentation yet on how to use it.  However, in the WebGrease package (which the ASP.NET optimization framework depends on), you’ll find the executable wg.exe.  This is what will enable build time (or whenever-time) generation of minified bundles down the road.  Expect more blogging to come on how to use this exe.

  • Anonymous

    Hi John – you’re right, my bad for not looking at what build I was using…consider that code a preview…

  • http://johnwsaunders3.myopenid.com/ John Saunders

    This does not work in the RC:

    ‘System.Web.Optimization.Bundle’ does not contain a definition for ‘CdnPath’ and no extension method ‘CdnPath’ accepting a first argument of type ‘System.Web.Optimization.Bundle’ could be found (are you missing a using directive or an assembly reference?)

  • http://twitter.com/demisbellot Demis Bellot

    Having compiling & minification in C#/.NET is nice, but will always fall behind the leading web platforms  (e.g. Node.js / Ruby) where all the popular web DSL’s (e.g. Less, Sass, CoffeeScript, Compilers/Minifiers) have native implementations.

    Which is why I’ve opted to go for an dependency-free, extremely fast, pure node.js-based solution with integration in ASP.NET MVC/VS.NET, that just relies plain flat text files – for easy configuration. 

    It can also run outside of VS .NET and on the command line (so you can refresh without an AppDomain restart). You can check it out at: 

    https://github.com/ServiceStack/Bundler

  • Anonymous

    I didn’t write about in this blog post – will talk more about CDN in a forthcoming post – but there is limited CDN support in RC.  Take a look at the following:

    bundles.UseCdn = true;

    var jq = new ScriptBundle(“~/bundles/jquery”)
       .Include(“~/Scripts/jquery-1.*”);

    jq.CdnPath = http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.2.min.js;

    bundles.Add(jq);

    We’ll use the CDN URL if it’s present – otherwise, we’ll generate a URL to the bundle on the origin server.

  • Mike

    +1 for CDN support.

    This is a strangely big whole that hasn’t been addresses in any of the presentations I’ve seen.

  • Anonymous

    it is a NuGet package, yes, but it is a NuGet package that is also shipping ‘in the box’ with Visual Studio and is a part of the default project templates.  Hence, the association..

  • James

    Unless I’m missing something, what does this have to do with VS 2012? It’s just a NuGet package is it not?

  • Anonymous

    you can set the media type in a bundle transform, but StyleBundle sets the media type for you by way of setting the default transform.  Can you tell me more about what you want to do?

  • Anonymous
  • Donatas Mačiūnas

    It it possible to define media types for style bundles?

  • Andrii

    Great! When will available in nuget?

  • Mike

    Awesome, good response to feedback

  • http://shareourideas.com/ Naga Harish M

    This is Great Post!
    Very useful for me. Thank you for sharing.

  • Pingback: June 4th What’s Happening Around Visual Studio | MSDN Blogs

  • Chris Martin

    I’d like to see a way to alter the rendered bundle URI for CDN support.