Quantcast
Channel: Development
Viewing all articles
Browse latest Browse all 10

Setting up an Optimised Deployment Version of your AJAX ASP.Net Application

$
0
0

With rich AJAX web applications one needs to look at optimising resources such as your CSS, JavaScript and image files. Yahoo has collected together a great collection of best practices that have been put into their YSlow Firefox extension. These should be taken as a guideline and not gospel. In general however the core principals are as follows:

  1. Keep server roundtrips to a minimum. The techniques for the various resource types are as follows:
    • JavaScript - combine them into a single file if possible. One has to make certain they are combined in the correct order in the file.
    • CSS - again merge them as far as possible.
    • Images - Use CSS sprites where one combines several images into one and uses the background image offset to display the one you are after. To create the sprites you can always use this online sprite generator.
  2. Reduce the size of each request. This is done in two ways:
    • GZip/Deflate compress text resources by configuring IIS and/or using an ASP.Net HTTP Module.
    • Reduce file sizes. The tools I use for each type are:
      • JavaScript - Jazmin provides pretty good minification by removing whitespace. If you want better minification then you should take a look at the Dojo ShrinkSafe compressor which will shorten the name of local variables and functions to reduce the file size even more. The downside to this is it makes troubleshooting a production issue more difficult.
      • CSS - CSSTidy not only reduces whitespace but optimises the CSS expressions as well.
      • Images - As PNGs are my image format of choice I use PNGGauntlet to reduce the image file size.
  3. Ensure resources are cached. If you know that the files are going to change infrequently then all you have to do is set IIS to enable content expiration for a folder/file to expire after a set number of days, say 10. This will ensure that the client browser caches the resources and does not re-download them. However depending on the frequency of your deployments and your server architecture you most likely want to ensure that your etags are setup correctly. These are used to indicate whether or not the cached browser resources have changed. When loading a page the browser will send a request to the server, with the etag, for each resource and if it has not changed then the server will only return a 304 status code. Note that this advice is contrary to Yahoo where they tell you to remove etags for a web farm scenario. What you should rather do is ensure that the etag change number is the same for each server in the web farm. This ensures that the etag generated for the same resource on a different server is the same (how to do this varies depending on your version of IIS - read this post for more IIS6 information). If you are using a single web server however the whole this is a non-issue for you.

Okay so we know we want to combine and minify our CSS and JavaScript files when we deploy to our test, staging and production environments, but we don't want to develop and debug our applications with monolithic files with no whitespace and cryptic names. The problem is that our pages contain references to these files and we are merge them them together for deployment our references will now be incorrect. Well this is quite easily solved using compiler directives, like so:

<% #if DEBUG %>
   
<script type="text/javascript" src='<%= ResolveUrl("~/JavaScript/File1.js") %>'></script>
   
<script type="text/javascript" src='<%= ResolveUrl("~/JavaScript/File2.js") %>'></script>
   
<script type="text/javascript" src='<%= ResolveUrl("~/JavaScript/File3.js") %>'></script>
<% #else %>
   
<script type="text/javascript" src='<%= ResolveUrl("~/JavaScript/Section.minified.js") %>'></script>
<% #endif %>

For CSS files you can either use the directives around alternative <link> elements or around @import statements, like this:

<style type="text/css">
<% #if DEBUG %>
   
@import '<%= ResolveUrl("~/Css/File1.css") %>';
   
@import '<%= ResolveUrl("~/Css/File2.css") %>';
<% #else %>
   
@import '<%= ResolveUrl("~/Css/CombinedStyles.minified.css") %>';
<% #endif %>
</
style>

There is a catch to this however, since the aspx pages are compiled separately, setting your application to compile as a release build will not cause the references to change. What you need to do is change the compilation setting in the application web.config file, which should be set to debug="false" in the production environment:

<compilation debug="false">
  ...
</compilation>

Now on to the merging and minification. This could be accomplished using MsBuild or a batch file, but a batch file is simpler, so I'll discuss that. On one of the build steps for the web site(s) invoke the batch file which then ensures that the optimised files are always up to date. So you want to add something like this to your pre/post build compilation step in the project property window:

image

I setup the "Build Scripts" folder to contain the optimisation programs as well as text files with lists of files to merge, for example:

image

You'll notice that I group the JavaScript files on the site by Section of the site and not by page. The granularity of file grouping really depends on the application, but by section or function is a pretty good starting point. Each line of the file will contain a file name, like this:

SubDir\File1.js
SubDir\File2.js

I don't need to specify the full relative path of the files, in respect to the build folder because when I develop web sites all JavaScript files are placed within a JavaScript folder off the root of the web site.

In my simplified scenario the "Optimise Web Resources.bat" batch file contains the following:

set OLDDIR=%CD%

:: Change to the current directory
cd/d %0\..

::Ensure there are no old temporary files lying around
del *.js
del *.css

::Create a single file for the JavaScript in the admin section
for/F "tokens=*" %%f in(AdminJsFiles.txt)docalltype"..\Web Sites\Site\JavaScript\%%f">> AdminCombined.js
type AdminCombined.js | jazmin > AdminCombined.minified.js
copy AdminCombined.minified.js "..\Web Sites\Site\JavaScript\AdminCombined.minified.js"

::Combine the Stylesheets into a single file
for/F "tokens=*" %%f in(CssFilesToMerge.txt)docalltype"..\Web Sites\Site\CSS\%%f">> CombinedStyles.css
csstidy.exe CombinedStyles.css --template=high CombinedStyles.minified.css
copy CombinedStyles.minified.css "..\Web Sites\Site\CSS\CombinedStyles.minified.css"

::Remove the temporary files
del *.js
del *.css

::Restore the old "current directory"
chdir/d %OLDDIR%

The one part of the batch file that confuses most people is the FOR /F "token=*" statement. That reads in a line at a time from the file specified in brackets and puts it in the %%f variable. The contents of each file is then appended into a single file using a "type SourceFile >> DestinationFile" statement. In the case of the JavaScript files they are then piped through the Jazmin compressor/minifier, the output of which is redirected in an output file, i.e. AdminCombined.minified.js. With the CSS files, the command line syntax is a little different, but the outcome is the same.

Hopefully the rest of the batch file is pretty self evident and there is enough information here to get you started on implementing the approach yourself. If there are areas that need more explanation please leave a comment on the blog post.


Viewing all articles
Browse latest Browse all 10

Trending Articles