ScriptHelper now a Nuget package, and managing your Javascript

For a while now I have been looking at different ways of managing javascript inclusion in web pages, and also managing the dependencies that each script inclusion requires. Furthermore, working with ASP.NET MVC and partial views, working with the ever increasing number of dependencies as well as ensuring that each partial view has the script it requires, can be a little challenging. At the very least messy and tedious.

Ideally, I’d like every page or partial view to be able to express what scripts it requires and let the framework care about removing duplication, minification etc.

So, after looking at the needs of our application, as well as the needs of others I developed the ScriptHelper component which is now available as a Nuget package. I had released an initial version some time ago, details are here.

This latest version has the following features:

  • Support for RequiresScriptsDeferred and RenderDeferred methods to allow you to specify script requirements as many times as you like, where ever you like and to have these requirements only rendered to the page when the RenderDeferred method is called. This makes it easy to include the RenderDeferred at the bottom of your master page so all deferred scripts are rendered then. These scripts can be minified and combined at this time as well.

So you can do:

@ScriptDependencyExtension.ScriptHelper.RequiresScriptsDeferred("jQuery")

and maybe somewhere else in the page or in another partial view

@ScriptDependencyExtension.ScriptHelper.RequiresScriptsDeferred("jQuery-validate-unobtrusive")
@ScriptDependencyExtension.ScriptHelper.RequiresScriptsDeferred("SomeOtherScripts") 

Then when the:

@ScriptDependencyExtension.ScriptHelper.RenderDeferredScripts()

is called, all the previously deferred scripts are combined, minified and rendered as one single file inclusion

  • Support for .Less so you can have variables and functions in your CSS. No need to use a .less extension as .Less is automatically invoked if it is enabled for the first time it is required.

Note: Although the project source is titled as an MVC script helper, it can also be used within Webforms without issue as it is a simple static method with no reliance on anything from MVC. I just happened to start coding it with an MVC project in mind.

Additionally, the helper fully supports CSS and .less CSS semantics and syntax.

There are other frameworks that do similar things but I created this one for a few reasons:

  • The code is pretty small and simple. Very easy to modify and extend to suit your own purposes. It currently uses the Microsoft Ajax minifier. If you dont like it, implement the “IScriptProcessingFilter” and replace the current minifier component with your own.
  • I liked the ability to express dependencies explicitly. The number of JS libraries to use grows every day and it can get tricky in large apps to easily see what is required and needed.
  • I didn’t find one that easily implemented the deferred loading scenario, took care of duplicates, and minification, and .less support. Maybe there is now, but I got started on it anyway. Or maybe there is one, but the implementation looked ugly. Either way, I wasn’t satisfied with the current offerings.
  • I like coding stuff Smile

Download the Nuget package from here.

Download the source from here.

For full documentation on ScriptHelper (minus the new features here), see this post.

A ReadMe.txt file is included in the package with all configuration details.

But .Net 4.5 will include a bundling facility to address some this. Why would I use this?

See Scott Gu’s blog post for more info on this feature.

Well of course you don't have to and if you were happy with the default way bundling works, it is probably worth sticking with that. I mean, you don’t want to include extra libraries if you don’t have to.

However the bundling supports expressing dependencies programmatically in code. it is ok, but I prefer the XML file. Easier to read and define IMO. Sure the code isn’t that hard to read, but it could be anywhere and takes a bit more analysis to see all of the dependencies any one component may need.

There is no support for .less files out of the box. I am sure this wont be far off as adding .less support is pretty trivial. However it is not in there by default.

Bundling will include what you tell it to when you tell it to. This is good, but I love the idea of views or pages being able to express their dependencies without worry of script duplication. The deferred script loading feature of the ScriptHelper was one of my main reasons for developing this. If you don’t use deferred script loading/inclusion, then ScriptHelper will assume you know what you are doing and include the script file immediately as per bundling.

The bundling feature will use .Net 4.5. If you are bound to lower versions of the framework, this is a blocker. ScriptHelper is compiled against .Net 4, however there is nothing that really relies on .Net 4 specifics. If you were targeting .Net 1, it could prove a little tricky as generics are used and a few other features, but migrating/compiling against lower versions should not be very hard.

 

Managing your Javascript

So how does this help manage your javascript in your projects?

PartialViews

Normally partial views, other pages either rely on a knowledge of what is included via the master page and what is also included on the content page itself in terms of scripts. A partial view may include its own scripts, provided that script is not included elsewhere to prevent duplicate script files being loaded.

Using ScriptHelper and DeferredScripts your partial view can include anything all its dependencies. So the partial view can use the “RequiresScriptsDeferred” method to express its dependencies. At some point in time, typically at the bottom of your master page/layout, you will call the “RenderDeferredScripts” where all the required scripts are rendered. Typically this is also used with script combination and minification so that only 1 include is rendered. All duplicate inclusions are ignored, all files are combined, minified, and .less filter is run over it if required. A single script include is generated and your done. If the master page/layout has already expressed dependencies (such as jQuery for example) and the partial view also expresses this dependencies, it is ignored.

This means each partial view, component or page can express all its dependencies that it requires without fear of duplication.

Script Dependencies

The way ScriptHelper works is by using a friendly name to group one or more script files together in the ScriptDependencies.xml file. The way this is all grouped is up to you. You can group by component (jQuery, validation, etc…) or even by page or anything else. There only needs to be 1 reference to the explicit name of your script, everything else is by friendly name so when a version number of a script changes, potentially changing the name of the script file, you only need to change the name of the file in one place.

In addition, the dependencies for scripts in the XML file are listed explicitly. There is no guesswork as to what script require what in terms of dependencies since it is listed for easy identification.

Finally, if you like having version identifiers on your query strings to assist with browser caching, this can also be expressed in the ScriptDependencies.xml file. This means you can have a script with http://host/script.js?v=123 where the v=123 is the version identifier. The ‘v’ and the ‘123’ are all declared in the XML file which is easily updated manually or via a build script.

 

So if you find yourself in Javascript inclusion and dependency hell, give this library a shot, it may help.

3 Comments

  • This looks cool, what happens with caching?

    I am assuming each pack of scripts will be cached, but if you are changing the included scripts a bit between pages they wont be cached.

  • Hi Lee,

    It is all cached based on what scripts are included. So for example, if you ask for jQuery, and jQuery.validate and your Customscript1.js, this gets combined and cached with a unique key. Then when you ask for jQuery and CustomScript2.js this gets combined and cached with a different key. The combination of scripts determines the key of the cache item. So if you include the same 3 scripts on multiple pages/requests, this all uses the same cached item.

    You can see this in the request made back to the server when scripts are combined. You'll see something like:
    http://localhost:55006/ScriptDependency.axd?v=1.0&scrptcmb=18467,8910
    where the numbers 18647 & 8910 represent unique items for each script and constitute how the cache key is defined.

    Note: Caching is only performed in Release mode. No caching is done in debug mode.

    Hope that makes sense.

  • Woops the link did not come through properly. I mean't to write something like
    ..../ScriptDependency.axd?v=1.0&scrptcmb=18467,8910

Comments have been disabled for this content.