Background
About 70-90% of the time spent waiting for a page on the
average web site is actually due to the number of HTTP requests required for
images, JavaScript, and CSS files! A key step in improving load times is
reducing the number of these requests. Another is reducing the amount of
information that has to be transmitted. This article explains how to minify
and consolidate CSS and JavaScript files.
Sample Project
The sample is created with Visual Studio 2010 and requires
AjaxMinifier and MSBuild Extension Pack 4.0. [Download
Sample]
Minification
Minification is the process of reducing the size of a CSS or
JavaScript file by removing whitespace, removing comments, and many other
optimizations. Microsoft provides a free tool called Ajax Minifier for this
purpose. Ajax Minifier can be run on the command line, used from a .NET
program via an assembly, or using MSBuild tasks.
Consolidation
Consolidation is the process of combining multiple files
into a single file.
Philosophy
The goal is to improve performance as much as possible
without disrupting the developer's normal processes. The ideal result is that
in development the original CSS and JavaScript files are used, but in
production there would be a single CSS file and a single JavaScript file for
the entire site. Sometimes people break up styles or script code into separate
files and only include specific files on specific pages, my approach instead
was to simplify by using the consolidated file(s) on all pages. Since the
files will be minimized, consolidated, compressed, and using expirations for
effective browser caching, it could be both easy to use and perform extremely
well. The minification and consolidation are done at project build time so
there is no impact on runtime performance (unlike some script combining
technologies).
Versioning and Expirations
When a user visits a web site for the first time the browser
cache will not have any of the site's files. On a subsequent visit to the web
site it would be great if the browser used the files in its cache and did not
have to ask the server for them. Sadly, by default, that is not what happens.
Since the browser does not know if the file is "fresh", it asks the
server if it has a newer copy of the file. If not, the server returns a 304
status code and no content. Unfortunately, the trip to the server to find out
the file has not changed is wasted time, especially when it is doing it for
lots of files. The server can setup expirations (using IIS response headers)
to indicate that a given file won't be changed for X days. Now the browser can
just use the content from the cache and not need to talk to the server at all!
What happens when you need to update a file? To force the browser to request
the new file, just update the file's name by adding a version number (like
jQuery, with "jquery-1.4.2.min.js"). The page references the new
file which won't be in the cache, so the browser will request the updated
version.
Minifying and Consolidating Process
The first step is to download and install AjaxMinifier (http://ajaxmin.codeplex.com/) and the
MSBuild Extension Pack (http://msbuildextensionpack.codeplex.com/).
The web site project file (.csproj in downloaded sample) is adjusted using
Notepad. Here are the steps involved:
1.
Create a folder named "client" to host the combined files
2.
Create a class file "forceRebuild.cs". It is
"touched" during each build to ensure that a new version number is
generated (even when only .css or .js files are changed).
3.
Open the "properties\assemblyInfo.cs" file, change the AssemblyVersion
from the default 1.0.0.0 to 1.0.* (this will create a new version number each
time the project is built)
4.
Open the sample file BuildInfo.txt, copy its contents and paste them
into your website project just under the "<!-- To modify your build
process," comment (need to either replace your BeforeBuild and AfterBuild
or combine with things you already have there)
5.
Adjust the "NetAssembly" attribute from the <MSBuild.ExtensionPack.Framework.Assembly>
element to point at your web site code behind assembly
6.
Adjust the list of CSS files and JavaScript files that need to be
minimized, and the files that need to be combined
7.
The <AjaxMin> element invokes the Ajax Minifier MSBuild task which
performs the actual minification tasks. The minified versions of the files
will be in the original folders with a ".min.js" or
".min.css" file suffix
8.
The standard <ReadLinesFromFile> and <WriteLinesToFile>
MSBuild tasks are then used to combine the listed files. The combined versions
are placed in the "client" folder and are named with the version
number from the assembly
9.
The <ItemGroup> at the bottom temporarily includes the new
combined files into the project so the Visual Studio Publish feature will move
them
Referencing Combined Files
Since the combined files will be used on every page, it is
added to the master page <head>. The sample code in the Site.Master uses
PlaceHolders to include either the individual files in Debug mode or combined
files in Release mode. The links are created based on the assembly version of
the site and will automatically pull the correct version after each build.
Expirations
Since the combined files have specific versions and are
changed on each build, you can setup very long expirations on the "client"
folder (year or more) because any time you modify that files they will have a
different filename and will force a request to the server. To set expirations
in IIS 7 you use the Internet Information Services Manager and click on the
"client" folder for the web site, double click the "HTTP
Response Headers", and on the right click the "Set Common
Headers…", then set the desired number of days for the expiration:
Notes
·
You can only move changes to files that are combined by building
and moving the entire web site. If you want to be able to move just a new
combined .css or .js file, then do not use versioning for the combined file and
don't use expirations on the "client" folder. In that case your
clients will always call the server to see if they have the latest file which
will cause unnecessary requests and 304 responses, but you can move just the
changed files.
·
If you don't want to list all of the source files for
minification or consolidation you can, you can use wildcards.
·
Not all CSS files can be consolidated. Ones that contain styles
for a specific browser version accessed by IE conditional comments would not be
good candidates for consolidation.
·
If you require references to file names that don't change
(reference from HTML pages or 3rd party sites), create an additional folder
that does not use expirations, and copy the combined files to that folder and
don't use version numbers in the file names.
Test!!!
Make sure that your new minified and consolidated files
still work the way they did before! No one likes a faster site that doesn't
work!
Conclusion
This article shows a very detailed use of minification and
consolidation that supplies many benefits. Our site users saw pages load
anywhere from 25-40% faster using these techniques. Your mileage, of course,
will vary. This technique uses all free tools and is easy to implement. Try
it out and see if your gain in performance is worth the effort!