úterý 10. května 2016

Bundling and minification in WebForms

I like javascript, and have several mini-javascript applications living inside webforms pages. If you write structured js code you quickly get several javascript files. I referenced these js files directly from the page mostly using the ScriptManager component. By default these files are not minified nor bundled together, which is not very professional, but you can live it.

Another problem rises when you update those js files. They are cached by browsers, and are not downloaded automatically from the server. Your new feature is not working... Home made solution is to rename every time the js file when it changes (and all of its references), this is fragile when you have lots of js files. Better solution would be to delegate this rename to the framework. This can be done by bundling which automatically adds a timestamp to the bundled js files and you get extra minification feature. Sounds good, but this works by default in aspnet mvc stack, not in webforms. Fortunatelly the feature is not part of the mvc stack, it's inside of the framework (4+).


First step is to reference Microsoft ASPNET web optimization framework using nuget.



Bundling is initiated from Global.asax Application_Start method.


BundleConfig.RegisterBundles(BundleTable.Bundles);
BundleTable.EnableOptimizations = true;
 
BundleConfig is a class where the bundles will be defined. It is recommended to put it to App_Start folder of the application, but I think it can be everywhere in the project

BundleTable.EnableOptimizations = true forces the framework to use bundles and minification every time (in debug too which makes debugging of scripts hard). By default the bundling works when the application is compiled to release mode, this call overrides this default functionality. The call is useful to check the bundling process in dev environment.

The bundles need’s to be called from web pages. They can be called directly using Scripts.Render. 

This renders the bundle directly to the page where the call is placed and it respects the debug/release mode of the bundling process. I’m using web user controls to modularize my applications, those user controls need to call these bundles, when the call is made this way, the script tag is rendered where the user control is on the page (somewhere in the middle of form). I don’t like this.

Another way is to use ScriptManager component. This can get into every user control on the page and can get the referenced scripts from them and put it to one place on the page. As a bonus it removes duplicate script references.




The disadvantage of this is, that it does bundling and minification always, it can’t be turned off… The way how to turn off at least minification is to Clear bundles transformation when in DEBUG mode in Global.asax.
 
This still does bundling but doesn’t minify => can be easier debugged in browser.

Another disadvantage I found is that ScriptManager cannot detect duplicate js files inside the bundles => when 2 bundles reference the same js file, it will be bundled to twice and downloaded twice. From the functional side of view this is not problem, because the lastly included javascript function will overwrite the former with the same name, but it can burden the bandwith.

The biggest advantage of bundling for me is that it automatically appends the bundle name with timestamp and the browsers download the changed scripts automatically.
 
The same thing can be done with css bundling, works the same way as in mvc, it is described everywhere on the web.