Wednesday, November 2, 2011

Optimize Web Apps with PNG

Problem:
When creating a web application for mobile devices, there are 2 additional things everyone has to deal with; low throughput and high latency. How can you reduce the size of the code and the number of HTTP requests at the same time?


Solution:
By combining a couple great concepts together, I have created a single solution that can reduce file sizes by 50-80% and reduce HTTP calls down to the bare minimum; 1 HTML file and 1 PNG data file. Sound too good to be true? Here are the steps I have put together to achieve the desired outcome.
  1. Merge & Compress JavaScript Files
  2. Merge & Compress CSS + Image Files
  3. Merge & Compress JS + CSS into Single PNG File
  4. Extract Original Assets from PNG File in Browser
I have written a relatively small bit of JavaScript code (3 kB) for decoding the PNG file on the browser. This ties the whole thing together, creating an extremely lightweight mechanism for transporting all required assets from the server to the client.


Process Overview

Lets take a look at each step in this process...

Assumptions:
  1. The client browser must support the <CANVAS> tag (For most mobile devices like iPhone, iPad and Android devices, this is not a problem.)
  2. Only image files referenced in your CSS will be compiled in the build process. Images referenced by an <IMG> tag will still work, but will not be included in this single file solution.
  3. A batch file will be used to build and execute steps 1-3.
  4. We will use this simple folder structure for this project.
Folder Structure

Step 1: Merge & Compress JavaScript Files
Step 1 - JavaScript


Most well written applications will have many JavaScript files that have been logically created for ease of support and maintenance. 
So, the first thing we need to do is combine all of these JS files into a single file called dev.js. This file is not compressed or minified in any way, which makes it ideal when debugging your application. 


The next thing we need to take care of is applying the actual minification that optimizes the code for production use. I have chosen to use YUI Compressor to take care of this, but there are many other great tools that could be used. (JSMin or Packer to name a couple)
* It's very important that you are following JavaScript syntax best practices to ensure there are no issues introduced in the minification process. See Douglas Crockford's JSLint tool for more details.


Here is the BAT file code I'm using to accomplish the tasks involved in this step.
::Merge all JS files
ECHO. > dev.js
FOR /F %%v IN ('dir js\*.js /b') DO (
 type js\%%v >> dev.js
)

::Minify JS file
java -jar tools\yuicompressor-2.4.6.jar --preserve-semi --type js dev.js > prod.js


Step 2: Merge & Compress CSS + Image Files
Step 2 - CSS


A common practice used by advanced web developers for minimizing browser HTTP requests is called CSS sprites. With CSSEmbed We can accomplish the same desired outcome without all the CSS image mapping difficulties. This step in the build process makes CSS Sprites a thing of the past by embedding the images directly into the CSS. 


Similar to the JavaScript files above we merge all the CSS files into a single dev.css file. Once we have this, we then embed the images using CSSEmbed and compress the resulting file with YUI Compressor to create out prod.css file.


Here is the BAT file code I'm using to accomplish the tasks involved in this step.

::Merge all CSS files
ECHO. > dev.css
FOR /F %%v IN ('dir css\*.css /b') DO (
 type css\%%v >> dev.css
)

::Embed images in CSS file
java -jar tools\cssembed-0.4.0.jar dev.css > cmp.css

::Minify CSS file
java -jar tools\yuicompressor-2.4.6.jar --type css cmp.css > prod.css

::Cleanup temp file
del cmp.css

Step 3 - Merge & Compress JS + CSS into Single PNG File
Step 3 - Create Data PNG


This technique was first introduced back in 2008 by Jacob Seidelin, and it's where all the magic happens. I have written a small .NET application that embeds multiple JS, CSS, HTML and XML files it into a single PNG file. To further optimize the image create, I'm using a great tool called PNGCrush to squeeze every last byte of of the data image.





Here is the BAT file code I'm using to accomplish the tasks involved in this step.
::Prep CSS data
ECHO ^<File2PNG type="text/css"^> > data.dat
type prod.css >> data.dat
ECHO ^</File2PNG^> >> data.dat

::Prep JS data
ECHO ^<File2PNG type="text/javascript"^> >> data.dat
type prod.js >> data.dat
ECHO ^</File2PNG^> >> data.dat

::Generate PNG file
tools\file2png data.dat data.png >> NUL

::Optimize PNG file
tools\pngcrush -rem alla -c 0 -q data.png prod.png

::Cleanup temp files
del data.dat
del data.png


Step 4 - Extract Original Assets from PNG File in Browser
Step 4 - Extraction


Now that we have all the server assets compressed into a single PNG file, and optimized for HTTP delivery, we need to figure out how to extract the data in the browser. I have written a small bit of code that can be used to dynamically load the data found in the PNG file, and gracefully fail back to support devices that don't properly implement the <canvas> tag. 


Here is an example of how you can gracefully load data in the browser using the file2png code that I have created.
<script>
file2png.onInit = function () {

 var rev = '2011-10-21',
     planB = function () {
      file2png.loadStyle('prod.css?v=' + rev);
      file2png.loadScript('prod.js?v=' + rev);
     };

 if (file2png.supported) {
  file2png.loadData('prod.png?v=' + rev, function (obj) {
   if (obj.error) {
    planB();
   }
  });
 } else {
  planB();
 }
};
</script>


Download:
I have created a sample application that uses this process and I have packaged up the code used to compile this application. Additionally I have added some error detection into the batch file for good measure. Feel free to play with this and provide any feedback as I'm always excited to improve this process.

No comments:

Post a Comment