Sunday, September 28, 2008

Make your Click pages load faster!

Yahoo published a list of best practices for improving web application performance.

Click Framework provides a PerformanceFilter which caters for some of these rules. However not all rules can be easily automated.

This entry will outline ways to apply rules which are not covered by the PerformanceFilter namely, #1 - Minimize HTTP Requests (by combining files) and #10 - Minify Javascript and CSS.

Rule #1 also mentions CSS Sprites, a method for combining multiple images into a single master image. CSS Sprites is not covered here.

It is worth pointing out that its not necessary to blindly optimize every page in an application. Instead concentrate on popular pages, for example a web site's Home Page would be a good candidate.

There are a couple of tools that are useful in applying Rule #1 and #10:

  • YUICompressor - minifies and compresses JavaScript and CSS files so less bytes have to be transferred across the wire.
  • Ant Task for YUICompressor - an Ant task that uses YUICompressor to compress JavaScript and CSS files.
  • JSMin - similar to YUICompressor but only minifies (remove whitespace and newlines) JavaScript files and does no compression at all. An advantage of JSMin over YUICompressor is that its faster and can be used at runtime to minify JavaScript, while YUICompressor is most often used at build time.
Below are some articles outlining how to use YUICompressor and Ant to concatenate and compress JavaScript and CSS files:
  • Article explaining how to use Ant and YUICompressor for compression.
  • Article outlining how to use a special YUICompressor Ant Task for compression.
Using one of the approaches above one can concatenate and compress all JavaScript and CSS for a Page into two separate files, for example home-page.css and home-page.js. Note that the two files must include all the JavaScript and CSS that is generated by the Page and its Controls. Then one can instruct Click to only include the two compressed files, home-page.css and home-page.js.

Click makes use of the utility class PageImports to include the CSS and JavaScript. PageImports exposes the method setInitialized(boolean), which controls when PageImports are fully initialized. Once PageImports have been initialized, no other CSS and JavaScript will be included.

Knowing this one can override Page.getPageImports(), and import the necessary JavaScript and CSS files and then set PageImports to initialized, forcing PageImports to skip other CSS and JavaScript files.

Here is an example of how to apply this in Click:
public class HomePage extends Page {

private Form form = new Form("form");

public void onInit() {
form.add(new DateField("date");
addControl(form);
}

public void getPageImports () {
PageImports pageImports = super.getPageImports();
String contextPath = getContext().getRequest().getContextPath();

String cssInclude = contextPath + "/assets/css/home-page.css";
pageImports.addImport("<link type=\"text/javascript\" href=\""
+ cssInclude + "\"/>");

String jsInclude = contextPath + "/assets/js/home-page.js";
pageImports.addImport("<script type=\"text/javascript\" src=\""
+ jsInclude + "\"></script>");

// Set pageImports to initialized so that no other CSS and JavaScript files will be included.
pageImports.setInitialized(true);
}
}
Using the following Velocity border-template.htm:
<html>
<head>
<title>Click Examples</title>
${cssImports}

</head>
<body>

...

${jsImports}
</body>
</html>

the rendered HTML will include one CSS and one JavaScript import:
<html>
<head>
<title>Click Examples</title>
<link type="text/css" rel="stylesheet" href="/click-examples/assets/css/home-page.css"
title="Style"/>
</head>

<body>

...

<script type="text/javascript" src="/click-examples/assets/js/home-page.js"></script>
</body>
</html>
A live demo is available here