Now that Visual Studio 2012 is done, we’re busy figuring out what we should do next. For Web optimization, there are a few key scenarios that we know we want to support moving forward:
Design and build-time optimization and generation of static, versioned files that can be deployed to the server/cdn/etc.
Virtual path provider support (this is to-date, the most requested addition)
Richer CDN fallback
There are also a whole bunch of other scenarios that we’ve either heard from different people or come up with during brainstorming. For example:
Inline script optimization
Work more easily with client side script loaders
Optimize HTML
Resolve relative paths that change when a resource is bundled (e.g. css that references an image using a relative path)
And many more…
So with the future in mind I have 2 questions for you. The first is a general, “what are you looking for?” Do some of the items listed above resonate with you? Is there something you want to make sure we’re considering?
The second question deals with a change in design philosophy for the optimization framework. When we first designed the framework, it was an explicit goal to limit the scope of modifications that we performed on your scripts and styles (limited, because minifying is obviously an operation that modifies your content). This is because the greater the delta that exists between the code you wrote and the code that gets pushed to the browser, the greater the difficulty in debugging.
However, several of the scenarios that we’re considering for future versions will require us to move further away from this initial goal. For example, to correctly resolve relative paths to images from within bundled CSS, we will need to modify the image URL. Optimizing inline scripts and/or HTML will require modifying the page content itself. These are just a few of many different examples of where developer content modification will be necessary.
So my second question is – is original content modification/rewriting an issue for you? Or is it one of those concerns that we’ve been carrying simply because it’s a concern we’ve been carrying?
I just merged my NodeJS port of the RestBugs sample application and pushed it up to github. In case you’re not 100% sure what RestBugs is, it’s an example of a hypermedia-driven bug management system (system including both client and service) that I originally created as a part of my REST Fundamentals course for Pluralsight.
The Node version is, for the most part, identical to the original ASP.NET Web API version. The only major difference is that the Node version uses a MongoDB database rather than a static collection running in memory. This was primarily driven simply by my desire to play with MongoDB.
My boss asked me this morning what my overall thoughts were comparing the 2 different frameworks and approaches. My comments went something like this: Because there’s less hand-holding by tools coupled with the fact that there’s 10 million ways to do any specific thing, Node can *feel* like it has a bit of steep “trial and error-like” learning curve. However, for those that get over that curve, I can already see that it will be freaking amazing and it will be tough to have patience for platforms that require more work in the name of safety.
That’s my 2 cents for tonight – take it for what it’s worth.
Now, a favor to ask.
For all of you folks who are further down this road then I am, I want to learn from you. If you have a couple minutes to spare, take a look at my < 200 lines and let me know how I can improve my code – and thereby my skills. There are a couple specific areas where I’m particularly interested in improving at the moment:
best patterns for error handling – particularly when I have all those nested closures
patterns for organizing my code (both modules and object design) – at the moment, everything’s in one file and my “domain logic” is module-scoped. This is workable because the sample isn’t that big, but I can see it becoming a problem for anything non-trivial. I had originally organized things into a “3-layer” type of design, with a Bug JavaScript object – but found it really painful constantly transforming JSON objects in and out. Would especially love to get your thoughts here.
good use of middleware – I was thinking that particularly for those post functions where I’m being sent an ID and I’m looking it up and working with a JSON object, it would be handy to push the lookup logic into a middleware function. Good idea? How does this work with errors or no value found?
other stuff jump out where I can improve?
I am confident that this code (as well as my Node skills) is a shadow of what it can be – but I wanted to put something tangible up there and then get pointers on where to look next.
I’m intrigued by node – partly because of all the recent hype and ‘cool kid’ factor – but mostly because I like the idea of having a single language context on both client and server. Plus, the overall programming style for authoring Web apis feels similar to the style I enjoyed in sinatra. At any rate, in an effort to better understand node, I’m porting RestBugs from ASP.NET Web API to node.
It feels like I’m actually close to a completely working application – however, there are a couple things (1 thing really) that’s keeping it from working, and before I go write a bunch of code to do something totally hacky, I would rather just ask you good people. Here’s a sample of the type of code in question.
Here, I’m getting a JSON doc from MongoDB based on the id that was sent from my form and I want to execute a function on it. That function is declared as a privileged method on a custom JavaScript object.
Now, you probably already know that the route handler code above won’t work. Why? Because MongoDB is giving me back a JSON object and I need to work with it as a Bug object. If I was using JSON instead of form URL encoding for my POST input, I would likely run into the same problem on that side of the fence as well. This leads me to my question:
What is the right way to think about moving between JSON input/output objects and “domain” objects?
My brain keeps telling me that my desire to map properties from one object to another (via my own code or a library) is wrong in a language like JavaScript, but I also came across some posts (http://erik.eae.net/archives/2005/06/06/22.13.54/) that seem to indicate that tricks like swapping out the prototype may be an even worse idea.
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:
Enable greater control over the default bundles that are registered with the Web application templates.
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:
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:
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:
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.
I got a question recently about my recent Pluralsight REST Fundamentals course. Several times throughout the course, I say something to the effect of “for more on X, check out the references section at the end of the module”. The question was – where are the references. The short answer is that each module has a PDF of the slides used – and the last slide is for references. However, to save you the trouble of going through each of the PDFs to extract the references, I’ll just provide a consolidated list for the entire course here. Hope this is helpful whether or not you check out the course – though I would love to get your thoughts on that as well!