II Loadtime
II Loadtime
Loadtime
JavaScript Performance Rocks!
by Amy Hoy & Thomas Fuchs
[Link]
Although we do a lot of open source
work, this book is not free.
Why this book costs money & why you should pay
We want to share our hard-won expertise with the world for
a reasonable price so we can keep on creating free and
cheap content, code, and art for everyone to enjoy.
If you’re going to optimize, you should always start here. There’s tasty
low-hanging fruit, just waiting to be picked.
Feel free to implement any or all of the suggestions here without the fear
of premature optimization. (Except for excessive minifying. You heard it
here first.)
2
Welcome to Loadtime
And one final technique, easy to say but not always fun to
implement:
3
Welcome to Loadtime
Perfect. That’s the right attitude for tuning your JavaScript-heavy web
app.
The biggest gains can often be had in the “non-invasive” realm: especially
good caching and file compression. And they don’t involve tweaking your
code, not even a little bit—it’s a win/win situation.
You can also use our DOM Monster tool and YSlow to identify loadtime
trouble areas & beat them into submission.
Moving Forward
Several chapters follow this one, each featuring one of the techniques
we’ll be using to tune your loadtime experience. These chapters all begin
with ‘Loadtime.’
4
Chapter 2
Script Load Order
the easiest & most effective fix, EVER.
Fun browser fact: the browser won’t begin to render the
very HTML and CSS of your web app unless all JavaScript
files are fully loaded (or just about).
For the fastest, simplest, and easiest performance increase you can get...
put your <script /> tags at the very bottom of your <body /> tag.
But it does mean that the browser won’t stop rendering the page while
it waits for the JavaScript files to transfer and do their thing—drastically
improving the perceptual experience for your users.
When the server behind this external stuff (or the network between you
& them) is slow or unresponsive—which seems to happen regularly to
various stats services—your app will seemingly take forever to load. With
the script tags at the end, at least, your user won’t be stuck with a blank
screen.
Makes sense, right? This is a rare side effect but it can occur.
Possible example: that autocomplete text field you have will appear
almost immediately, but not necessarily function right away. The strength
(or even presence) of this effect is completely determined by bandwidth
and browser speed. When everything’s snappy, it probably won’t be an
issue at all.
6
Chapter 3
The Cachét of Caching
So Good, It’s like Christmas for your Software
Caching should be the second tool you reach for, the
moment your application goes live.
Browsers have cached files automatically since the days of dial-up, but
they’re not the smartest cookies in the cookiejar.
Expiration Headers
Specifically, you want to set the expiration headers on your content files
that don’t change often—CSS and, of course, JavaScript files. Server Access Required!
These techniques require you to
The goal is to have the browser keep those copies of these files until have access to your web server
they change, so that the latest version is always right there on the user’s configuration. So get to it!
computer. Or order your hosting company to do
it for you.
This single change can save tens of time-wasting requests per page view.
Loadtime: The Cachét of Caching
In this case, the file was downloaded on July 22nd 2008 and will be held
until July 22nd, 2013—5 years in the future—at the minimum.
The browser will then check to see if the file has changed and, if so,
download the new version. If no changes have occurred, it’ll keep happily
humming along with the cached copy.
Cache-Control: max-age=157680000
8
Loadtime: The Cachét of Caching
Caching Strategies
There are two major strategies for caching JavaScript and CSS assets:
• very long cache periods (e.g. months or years; “far future cache”)
• short or medium cache periods (days) (“short cache”)
They’ve both got their pros and cons, and, unfortunately, both require a
different support system to work right.
For sake of argument, let’s pretend we’ve got this interesting little file
that we want to cache and it’s called our_app.js.
This seems ideal, because then your user’s caches will be safe and their
experience will be snappy until many happy months go by.
But meanwhile, back at the data center, you want to roll out your spiffy
new psychic autocompleter—but nobody will know because you originally
set a far-future cache expiration date to 2010.
9
Loadtime: The Cachét of Caching
The browser will say “Hey, I’ve got [Link] but not [Link] in my
cache, it must be new” and slurp it down fresh.
ourapp_2.[Link]
Ruby on Rails does this automatically by adding a query string to the end
of the base filename, that reflects the last modification date of the file:
[Link]?20080822
You can take whichever approach you like, or think up your own.
As long as you change the filename when you need to push changes to
your users, far-future caching will work for you.
When that happens, your user’s browser will ping the server for the 10
Loadtime: The Cachét of Caching
If the date’s different than the copy it’s already got saved, the browser
will download it fresh.
The browser will always check for the same file name. If you cache
[Link] with a 24-hour maximum age, the browser will ask for the
headers for [Link] every 24 hours. Rinse and repeat.
You should stick to far-future caching unless you don’t have control of
your app’s filenames.
11
Loadtime: The Cachét of Caching
Add the following (version appropriate) code to your conf file if it’s not
already there.
Be sure to place the above snippet inside the correct block for the
domain name/application you’re configuring.
12
Loadtime: The Cachét of Caching
Short cache horizon (24 hours for JS, 5 days for CSS):
ExpiresActive on
ExpiresByType text/css “access plus 5 days”
ExpiresByType application/x-javascript “access plus 24 hours”
ExpiresByType text/javascript “access plus 24 hours”
FileETag none
13
Loadtime: The Cachét of Caching
Configuring nginx
If you’re using nginx, it’s simply a matter of putting this nice concise
snippet inside your server{} configuration block:
Short cache horizon (24 hours for JS, 5 days for CSS):
location ~* \.css$ {
if (-f $request_filename) {
expires 5d;
break;
}
}
location ~* \.js$ {
if (-f $request_filename) {
expires 24h;
break;
}
}
14
Loadtime: The Cachét of Caching
The easiest way to avoid this problem is to give your JSON and
generated JavaScript code a different file extension.
You can use the Safari, WebKit or Chrome Inspector, Firebug with
Firefox, or the Charles proxy to double-check that you’re sending out all
the right headers with your assets. And you can use YSlow (with Firebug,
with Firefox) to be sure that the caches are having the desired effect on
your page weight.
If you glossed through the “Is Your App Behaving Badly?” chapter, now’s
a good time to go back to it!
15
Chapter 4
Con-ca-te-nation
Fewer Files is Better.
Most web apps are composed of scores of little files. It’s
just convenient to write them that way, and it makes
it easy to find the code you’re looking for when you’re
developing.
Here’s another tasty low-hanging fruit: Reduce the number of files you
pump to the browser.
IMG
:FE:LII<EK N8@K@E>
CSS
The browser won’t start fetching the next batch of files until the current
batch are done.
you don’t, you should); doing it for JavaScript can only help.
The trick is to structure your files effectively for both development and
production environments: when you’re writing code, break out the files
however makes sense to you; when you push to the server, smush the
files together, preferrably into one file. Be mindful of the order of the
code in the file(s) is sane.
What to Concatenate
Your project probably has some combination of library files, original
code, and data in JSON or other JavaScript format.
This goes for all the files for any JavaScript libraries you may be using—
Prototype, jquery, mootools, YUI, [Link], & so on—as well as your custom
code.
Concatenation Counter-indicators
Concatenation isn’t always the answer. If the code is both A) weighty
and B) used only on a select few areas of your app, it may not be a good
candidate to roll into your monolithic JavaScript file.
For example, a date-picker might be a few hundred lines and yet only
used on one or two screens. In this case, you may choose not to have
it in the main file for the entire app; it may make more sense to include
it on those areas only, or load it dynamically in the case of a more 17
Loadtime: Con-ca-te-nation
interactive application.
Concatenating on Deployment
Deployment time is the best time for concatenation. And you don’t have
to do it by hand any more.
It’s not hard, really, with any scripting language that’s meant to be
embedded inside HTML (or other surrounding code). Just add yourself
some require() functions, or whatever the language of your choice uses,
and be sure they are parsed in the correct order.
But, as we just told you, you don’t have to do it by hand any more.
Sprockets is a lovely tool from the Prototype Core team, built specially
to smush all your JavaScript files together on command. With Sprockets,
you can split up your code into as many JavaScript files as you like, and
then Sprockets will join them up for you when you deploy your app—
either by hand, or with your deploy script.
Bonus: Sprockets will handle all of the inter-file dependences for you.
Installing Sprockets
Sprockets is written in Ruby and distributed as a Ruby gem. But it’s MIT-
licensed and someone has also made a PHP version (see below).
To install the original Ruby version, you’ll need the Ruby language
environment and RubyGems, the package distribution system. If you’re
running on OS X, you already have both of these goodies. Otherwise,
you may need to install them first.
19
Loadtime: Con-ca-te-nation
Using Sprockets
Add dependencies to your JavaScript files, just like you would for Ruby or
any other language.
Or…
The key difference between the two forms (quotes or angle brackets):
• angle brackets will search your load path iteratively for [Link]
and load the first one it finds;
• quote marks will attempt to load CURRENT_DIR/[Link]
immediately (no searching)
Say what? We know, it's confusing. Turn the page for a friendly example.
20
Loadtime: Con-ca-te-nation
javascript/
[Link]
lib/
[Link]
Quote marks won’t work because the [Link] library is not in the
same directory as the file that calls Sprockets, it’s nested.
Running Sprockets
After you’ve read the rest of the docs (see the sidebar!) and set up your
project, you can process it up with the included sprocketize command-
line tool:
This will grab all the .js files from the javascripts/ folder, concatenate
the contents according to the rules you’ve set, and the result will be
[Link].
21
Loadtime: Con-ca-te-nation
You can use this approach to reuse your own custom JavaScript
components across multiple proejcts, without making a copy each time
you want to use them. (Yay! DRY!)
Remember that when you use //= require <[Link]> with angle
brackets, Sprockets will search for the first matching file inside your
load path… and if you use quote marks, //=require "[Link]", it will
automatically try to load that named file in the directory of the including
file.
It will do a significant amount of the work for you! Check out the docs
for details.
[Link]
It looks like he’s got a couple features planned that aren’t implemented
yet (the gzip directives, for example). Github’s a great way to follow his
progress. Maybe even nag him a bit. Or, better yet, contribute.
ext/[Link]
Not sure where that is? Hit up your command-line with the following
command to get the path:
Sprockets has a number of other toys in its sandbox, and it’s sharing
them aaaaall with you.
• clean out your code comments (selectively) from the final result
*PDoc
• generate documentation (PDoc-style*) from special structured
PDoc is a JavaScript documentation
comments system that generates really nice looking
• bundle assets, like CSS and images, for JavaScript plugins and useful HTML-based documentation
• insert string constants into the final result, e.g. versions, copyright info, from special source-code comments.
etc. [Link]
24
Chapter 5
Inlining & Precaching
Like Inline Skating, but without the scabbing
Mama always told you to separate your concerns. Or was it
that there’d be days like this?
Anyway.
Common wisdom in web development circles says: separate content
(HTML) from presentation (CSS), and to keep both of those faaaaar
away from dirty old function (e.g. JavaScript code).
And just like every other kind of common wisdom, there are exceptions.
Brace yourself, because we’re about to tell you that sometimes it makes
sense to smush your CSS and JavaScript into the very same file with your
HTML.
We don’t mean squishing your CSS
Inlining Isn’t Evil into attributes inside your HTML tags
Sometimes smushing your CSS and JavaScript into one file with your (or JavaScript, either).
HTML—called inlining—is not merely okay, but actually really beneficial.
We mean inlining the whole contents
It’s natural for this to feel wrong. Just know that it’s right. of files. In the appropriate places.
Why Inline
The point of inlining is three-fold:
When to Inline
Based on these benefits, there are a couple scenarios where inlining
makes great and perfect sense:
A simple page that absolutely must have top performance all the time
Example: Google’s search page, which is, in fact, inlined out the wazoo.
Or in the wazoo. Whatever.
26
Loadtime: Inlining & Precaching
How to Inline
Inlining can be tougher than it sounds. There are three major steps. Are
you with me?
Good.
First, open your CSS and JavaScript files for the page you plan to inline.
Second, open up the HTML source for that page.
Remember that your JavaScript still
needs to be positioned in the right
Paste the CSS and JavaScript in (in their appropriate locations).
locations in your HTML file; still,
ideally, at the end.
Save.
But seriously, if your to-be-inlined area is a big complex beast and you’re
more comfortable working with the files separated, you may want to
consider a deploy hook that will compile them for you.
I hear you: Is that like reading the user’s mind and giving them the
cached files before they even ask for them?
27
Loadtime: Inlining & Precaching
In a word… yes.
What’s Pre-caching?
Pre-caching’s a sneaky little low-tech trick that can definitely make your
user’s experience seem faster.
1. your customer loads up a page that they have to get through, like a
sign-in or country selection page (reserving judgment on the wisdom
of country selection pages)
2. while he’s busy fiddling with the thing he needs to do, you’re loading
JavaScript and CSS files in the background—files that aren’t even
needed on this page!
3. once your customer’s done with that page, the external stuff
necessary for the next page will already be loaded in his cache
Why Pre-cache
According to Yahoo!’s research, at any given time, 20% of users will have
a “no-cache” experience when visiting your web app.
If you assume those numbers are globally accurate, that means that 20%
28
Loadtime: Inlining & Precaching
So if you have to put a road block in their way anyway (like a sign-
in page), why not take that opportunity to speed up their subsequent
experience?
How to Pre-cache
The simplest way to pre-cache is to simply include the JavaScript or CSS
files at the bottom of the roadblock page, just like you would on any
other page.
29
Chapter 6
Under Compressure
JavaScript Compression, Not a David Bowie cover band
This section will be fun and confusing because of the
terminology. Let’s have a word definition war!
There are essentially three ways to squish down your JavaScript code
files, divided into two camps: the types that modify your code, and the
types that do not.
You shouldn’t “minify” your JavaScript with any of the tools that add
obfuscation, because the client has to decompress it with JavaScript. This
can be slow.
If you really want to reduce the size of the content of your JavaScript Minifying and packing are last-
files, we strongly recommend you stick to the well-behaved white-space ditch efforts. You should try the
removal, variable renaming type. other stuff (cleaning up your code,
caching, getting a faster server,
But, except in extreme circumstances, you may as well just stick to gzipping, etc.) before trying to
gzipping. minify the heck outta your files.
That’d be premature optimization.
BIG HONKIN’ EXCEPTION: the iPhone is hardcore about
caching—for obvious reasons, it being a little handheld device and And we all know what that means.
all. To persuade iPhones to cache your stuff, you need to keep
each file under 25k. This is where minifying can come in handy.
See [Link] for more
info on this.
Good Minification
I’ll assume we’ve suitably scared you about minification vs packing, and
all the fallout that may ensue if you confuse one for the other. But just in
case… good minification does not obfuscate your code!
Now we’re set on what good minification isn’t, but how about what it is?
function upcase(string){
// this is a pretty useless comment as
// it is obvious what is going on
return [Link]();
}
Given our longer example above, this is the actual result that the YUI
compressor worked out:
For example, the Compressor doesn’t like multiple var statments inside
one method, & it will tell you so. It’ll also identify dangling variables that
are declared but never used, and other such good-to-know things.
function upcase(string){
var a = 1;
var b = 2;
return [Link]();
}
34
Loadtime: Under Compressure
It’s just like having a smart, well-meaning, but slightly passive aggressive
friend looking over your shoulder, all the time!
But seriously, the Compressor gives good hints. It’d do you good to listen
to it.
35
Loadtime: Under Compressure
Gee! Gzipping is the Answer Want the technical dirt on the gzip
Gzipping is the best solution for JavaScript file size, bar none:
algorithm? Be careful what you ask
for: [Link]
• You can get a 1:4 reduction in size with gzip. That’s from 4K to 1K,
or 40K to 10K.
• Gzipping doesn’t remove white space, or alter your variable or
function names, making it easier to debug
• Gzipping is done by your web server on the way out, meaning you
can configure it & forget it (after testing, of course)
• Gzipping offers a low performance hit compared to script
obfuscation
Don’t forget that gzip is the same compression method used for GIFs and
PNGs (and zip files, too, of course). Web browsers are already all over
that. It’s not some crazy new hippie web 2.0 thing.
Combine this with proper caching settings, and users will download your
gzipped JavaScript, it’ll get unzipped by the browser, and that unzipped
source will get cached for as long as you need.
36
Loadtime: Under Compressure
ExpiresActive On
ExpiresByType image/gif "access plus 5 years"
ExpiresByType image/png "access plus 5 years"
ExpiresByType image/jpeg "access plus 5 years"
ExpiresByType text/javascript "access plus 5 years"
ExpiresByType application/x-javascript "access plus 5 years"
ExpiresByType text/css "access plus 5 years"
Weell… If you’ve got a lot of custom JavaScript to gzip up, you shoulder
consider switching up your hosting situation.
There’s only so much magic we can work for ya, you know.
But, I hear you saying, I don’t have a lot of custom JavaScript. Just this
honkin’ big JavaScript framework! It’s not even mine!
Aside from not having to do the work yourself, this centralized hosting
means a potentially download-free experience for your users.
For example, Joe visits Web App A, and it uses Google’s hosted
Prototype [Link]. Then Joe visits your Web App B, which also uses
Google’s hosted Prototype [Link].
Joe’s browser only downloads the library once, the first time. Whoopee!
38
Loadtime: Under Compressure
Offered Libraries
As of time of writing, the libraries included are:
• jQuery
• jQuery UI
• Prototype
• [Link]
• MooTools
• Dojo
• SWFObjectNew!
• Yahoo! User Interface Library (YUI)New!
Nice!
39
Loadtime: Under Compressure
How it Works
To use Google’s hosted libraries, just reference the libraries like this:
<script src="[Link]
prototype/[Link]/[Link]" type="text/javascript"></script>
Generally speaking, it makes sense to use your own libraries if you can
get the server properly configured for caching, and if you are deploying
an app on a long-term basis.
40
Loadtime: Under Compressure
Translation: if you’re charging money for your app’s performance (or your
reputation’s riding on it), we recommend you suck it up and get better
hosting.
But Google’s hosted libraries are perfect for quick projects, and non-
mission-critical apps when you’ve got inexpensive hosting without access
to web server configs.
[Link] 138k
[Link] Minified 80k
[Link] Minified+GZip 24k
[Link] 120k
[Link] Minified 56k
[Link] Minified+GZip 19k
0k 25k 50k 75k 100k 125k 150k
41
Chapter 7
Cover Your Assets
an EASY way to increase Zippiness
Streams, as in data flowing and burbling over picturesque
little rocks, and concurrent, as in more than one at a time.
Quick review. Most of us tend to think (& therefore operate) as if the
browser request cycle looks like this:
k_\j\im\ii\Z\`m\j
2 gifZ\jj\jk_`ji\hl\jk
L
HTM
pfliYifnj\i
IMG
3 [fnecfX[jXccXjj\kj
pfliYifnj\i
1 i\hl\jkjXi\jfliZ\ JS
CSS
If only it were so! But it is not. In fact, Step #3—where your browser
downloads all assets (aka supportive files)—is much more complicated
than that.
HTM
L
:FE:LII<EK N8@K@E>
IMG
JS
CSS
Browsers will only open so many connections with one host (e.g. www.
[Link]) at once. We call these concurrent streams. And the
default most browsers use is criminally low: 3 concurrent streams, maybe
4.
43
Loadtime: Cover Your Assets
And yet in 2009, it’s a common loadtime performance issue for web
apps with lots of little files.
It’s easy to see how this can have an unfortunate impact on the zippiness
of your customer’s experience with your web app.
This leaves us room to get tricky: You can work around the annoyances
of browser’s concurrent stream limit by using multiple asset hosts.
9<=FI<DLCK@GC<8JJ<K?FJKJ 8=K<IDLCK@GC<8JJ<K?FJKJ
8%[fdX`e%Zfd 9%[fdX`e%Zfd
:FE:LII<EK :FE:LII<EK
:FE:LII<EK N8@K@E>
45
Loadtime: Cover Your Assets
If you reference the same asset with different hostnames, the browser
can’t tell that the assets are identical… and all your work on caching will 46
Loadtime: Cover Your Assets
In this case, for example, your user will have to download the image
twice, rather than pulling it from cache the second time:
The best way is to build a checksum from the filename of the asset and
calculate the modulo for the number of asset server hostnames you use.
Keep it around to verify.
47
Loadtime: Cover Your Assets
• the extra DNS lookups will defray the benefits of the faster loading
cycle
• you’ll also have to spawn more network connections than necessary
• and almost all servers are already configured to use the HTTP 1.1
keep-alive directive, which means your server will keep sending files
down the pipe as long as the connection’s open
Of course, if you follow the rest of our advice, this doesn’t matter for
your JavaScript files. You should only have one of those!
48
Chapter 8
You Need an Upgrade
C’mon, don’t you watch MTV Racks?
If you’ve already got a high-octane hosting environment
with a slew of slices and performance monitoring up to
here, you can skip this section. Carry on!
There comes a point in every app’s life when it simply outgrows its
humble shared hosting origins.
It’s not just page weight and open streams and DOM churning that can
slow your app down.
If your web app views are taking a really long time to compose because
of complicated queries, slow libraries, or simply very high server load (or
very low bandwidth), there’s no amount of JavaScript tuning you can do
to offset that.
• the longest wait is from the browser request to when files start
downloading
• pages get to the “mostly complete” phase but the browser keeps
waiting on
• the final bits (watch out for remote JavaScript)
• pages render fast once they’re downloaded (bandwidth problem)
• the Net tab of Firebug doesn’t indicate that download speed is
the issue (in the case of a back-end performance problem, not a
bandwidth problem)
Rails
FiveRuns [Link]
New Relic RPM [Link]
50
Loadtime: You Need an Upgrade
Django
Profiling Django Applications
[Link]
Quick profiling your Django web site with debug_toolbar
[Link]
PHP
Improving Performance by Profiling PHP Applications
[Link]
PHP Performance Profiling
[Link]
XDebug
[Link]
51
Chapter 9
Reduce Complexity
Tis a gift to be simple, especially for Page Rendering
A complicated DOM is an unfortunate beast. Hard to read,
hard to parse, slow to render... and it’ll slow your JavaScript
down, too.
When it comes to the DOM—that is, the structure of a document’s HTML
and the API that browsers supply for manipulating it—simpler is better.
Unlike that foregoing sentence.
Simpler means fewer DOM nodes in the DOM tree, which means faster
sites.
Simplifying your DOM can really affect the runtime performance of your
app, too, but it also affects your loadtime performance. More complex
DOMs take up more bytes, and unnecessary nodes (especially nested
divs) can add a lot of rendering time to your user’s experience.
• General complexity.
• Browser-specific complexity.
Loadtime: You Need an Upgrade
General Complexity
Here’s an obvious (and simplified) example of an unnecessarily complex Reduce CSS complexity. Simplifying
set of nodes: your CSS can make your rendering
zippier, and significantly improve your
<p> animation speed. Get yourself down
<b>This is bold</b> to as few rules as possible, and avoid
</p> having many rules that overwrite
other rules (for example, through
This example has two elements (p and b) and three text nodes excessive use of !important). It’s a
(whitespace and the text in the b tag). good practice all around!
Yep, that’s right kids—with that one stroke, you’ve axed half the elements
and 2/3rds of the text nodes. But, as we said, that is a simplified
example.
53
Loadtime: You Need an Upgrade
Browser-specific Complexity
Lots of people—purists—will tell you that sniffing browsers is a terrible
idea. But in certain situations, terrible ideas can actually become very
good ideas, indeed.
There are times when only one type of browser (coughIEcough) requires
additional complexity, when the others are chugging along just fine.
54
Loadtime: You Need an Upgrade
When one really needs to skin a cat, one can only be so squeamish about
the tools one uses to do the skinning. ‘
55
Chapter 10
JavaScript, On-Demand!
Right where you want it, when you want it.
If you’ve tried everything else, on-demand JavaScript may be
an option for you.
Sometimes you only rarely have need of certain sets of code. But these
bits of code might cause your initial page loading time to creep up
towards the unacceptable zone.
That feedback form that’s just used once every 1000 pageviews -- spare
your users the cruft and load it on demand.
Once you’ve prepared that, you simply insert it into the DOM (in the
Loadtime: JavaScript, On-Demand!
proper place):
// insert into the dom (at the end of the body element)
[Link](script);
That’s all. Your JavaScript will be loaded. Keep in mind that there won’t
be any events called (the DOM is already loaded), so just initialize things
directly at the end of your JavaScript file.
Caveat: mind that the protocol used to load in the JavaScript file should
be the same as for the containing page, so if you’re on a secure site and
use SSL, use https:// to load in the file.
If you’re using Google Analytics, you might have seen the following line,
that you might want to ‘borrow’ and use for your own evil purposes:
57
INTERMEZZO
SSL & Serving JavaScript
Not Quite a Chapter, More than a Sidebar
SSL is ever-more popular among web services, so you have
to be ever-more vigilant about your JavaScript serving.
There are two brick walls you might hit with your rich app and SSL:
There are exceptions… kinda. With Safari only, it’s possible to serve
assets over plain HTTP, when the main connection is SSL.
But your users aren’t all Safari users, are they? Then that exception
doesn’t help you at all. Better to not push your luck.
You’ll know you forgot to fix this problem if your users write you about
scary “mixed secure/non-secure content” warnings.
Loadtime: JavaScript, On-Demand!
Cache-Control: public
In addition to the proper Expire headers, this tells the browser that it’s
okay, go ahead and cache it, because it’s not a potentially sensitive file.
Our sample configurations do this. Check out the Goodies folder for
more.
59
Chapter 11
Smush Those Images!
It’s like shrinky-dink, for the web—no Fireworks required
Everybody used to talk about optimizing images. Nobody
talks about it any more.
We think this is because they’re too busy thinking up ridiculous Web 2.0
app names. It’s certainly not because the tools are so much better than
they used to be.
[Link] gives very, very good results. You can easily shave 25% off
your PNGs and JPGs without even a teeny bit of quality loss.
You could try to figure out how to do it yourself with the various
command-line tools used by [Link] on the back-end, but it’s a heckuva
Loadtime: Smush Those Images!
lot more fun to just smush. And it’s fun to say. Smush smush smush!
Installing smusher
To install smusher, you need Ruby, RubyGems and curl. On OS X, you’ve
already got ‘em.
$ smusher /path/to/your/images
If you have a lot of images, this might take a while. Go get some coffee.
Using [Link]
You can Smush on the web, without installing a thing. Upload your
images on [Link]
You can also run [Link] on your app from the Tools tab in YSlow.
61
Loadtime: Smush Those Images!
Smusher will convert GIFs to PNGs, although you’ll have to update your
asset paths.
62
Chapter 12
Favicons Are Ridiculous
The most ridiculous performance tip EVER
Somebody, somewhere, ought to fix this problem—but until
they do, you should work around it.
Did you know that browsers will check for a [Link] file on every
page load?
Custom favicons are a great idea, anyway. Even if you’re too busy or
strapped to create one that reflects your logotype, put up one with a
color that matches your UI. Or even a blank one. Whatever it takes!
Making a Favicon
Favicons are 16x16 pixels, and the path must always be /[Link]
unless you specify otherwise (see below).
Loadtime: Favicons Are Ridiculous
Referencing a Favicon
You can either leave your [Link] file in the root directory of your
web app, or you can change its name and specify a location using this
link tag:
64
Chapter 13
CSS Sprites are Magical
Tired: Image Maps. Wired: CSS Sprites.
Do you remember how, in 1996, image maps were the
hottest thing ever? Yeah, CSS sprites are like that. But in an
alternate reality.
Image maps and CSS sprites are, in a way, flip sides of the same coin.
The key difference lies in the application. For image maps, the whole
image is meant to be seen by the user, and you use special HTML
coordinates to say which areas are clickable and what they do.
Google Uses Sprites
Sprites Save Silly Amounts of Time
With CSS sprites, the full image is never revealed to your visitor—it's
like a junk drawer of interface elements, and you use CSS to select the
right bit for the job, when and where you need it. You set the full image
as the background image for a DOM element, and then you use those
coordinates to select only the part of the image you want to reveal.
CSS Sprites are Magical
• icons
• buttons
• anything small with hover/depress/inactive states
• bits and pieces used to build page frames, button frames, gradient
changes, etc
It's a bad idea, on the other hand, to do sprites for images that are rarely
used together, lots of photos, and so on.
66
CSS Sprites are Magical
67
CSS Sprites are Magical
But if you don't endure the annoyance in this stage of the process, you'll
have greater annoyance when you get to the code-writing portion. That's
because the annoying part is making sure your separate elements are
aligned and spaced properly. If they're not, your CSS is going to be a real
pain in the butt to write.
The key is to lay everything out logically, nice and regular. This way you
can determine the various “windows” using math, rather than individually
measuring each time.
68
CSS Sprites are Magical
div#nav [Link] {
background-position: -116px -44px;
}
69
Chapter 14
Stop! Don't Close That Tag!
If Google jumped off a bridge, would you do it too?
If your answer is “yes,” then you almost surely want to
consider this fairly extreme technique! Remember, the fall
won't kill you—it's the sudden stop at the bottom that does
it!
Here's a whacky trick that is way less wrong than it seems. But it does
require you to write your app with an HTML4 Doctype in mind.
Continued...
Stop! Don't Close That Tag!
• </body>
• </colgroup>
• </dd>
• </dt>
• </head>
• </html>
• </li>
• </option>
• </p>
• </tbody>
• </td>
• </tfoot>
• </th>
• </thead>
• </tr>
71
Stop! Don't Close That Tag!
• </area>
• </base>
• </br>
• </col>
• </hr>
• </img>
• </input>
• </link>
• </meta>
• </param>
Entirely Omittable
And there are big, seemingly important tags that you can omit entirely:
• <html>
• <body>
• <head>
Yes, it's true, according to the spec. You can do it. And Google does, as
part of their extreme tuning package.
72
Stop! Don't Close That Tag!
For a mini tech video on this topic, check out Google's page “Reducing
the file size of HTML Documents”:
[Link]
73
Chapter 15
Screw IE & Invent Custom Tags
In case the other techniques aren't radical enough for you
In a world, where violence is king... wait. Is this the wrong
script? In a world, where the W3C is king, you're willing to
do whatever it takes, to break all the rules... That's better.
Want to do some extreeeeeeeeeme DOM shrinkage, and don't give a fig
for IE (and give only half a fig for maintainability)? Well, you could just
invent your own tags.
Say what?
Yeah, you heard us right: Invent your own tags, W3C be damned!
Browsers don't care about tag names. Existing tag names are, for
the most part, nothing more than bundles of default CSS rules and
behaviors.
You can make up your own and the browsers won't blink. (Except IE.
What a surprise!)
Screw IE & Invent Custom Tags
<p class="important">
<em>lorem</em> ipsum
</p>
<x>
<y>lorem</y> ipsum
</x>
<style>
x { display: block; font-weight: bold }
y { font-style: italic }
</style>
You can shave quite a few bytes off this way, with the same results and
no added client-side complexity.
Of course, as we said, it won't work in IE, and it's more than a bit
extreme. But if you've got a need for hyper-optimization and few IE users,
it might work out well for you.
75
JavaScript Rocks! presents...
JavaScript
Performance
Rocks
by Thomas Fuchs & Amy Hoy