Professional Documents
Culture Documents
Kick-Ass
Web Apps
(with focus on JavaScript)
Jerome K. Jerome
Three Man in a Boat
The Life of Page 2.0
HTML page
request onload settles request
sent
marriage? R.I.P.
conception birth graduation
User perceived
“onload” happens
somewhere here
The waterfall
The Waterfall
1. Less stuff
2. Smaller stuff
3. Out of the way
4. Start early
The Waterfall
1. Less stuff
2. Smaller stuff
3. Out of the way
4. Start early
Less HTTP requests
• Combine components
Less HTTP requests
• Before:
<script src="jquery.js"></script>
<script src="jquery.twitter.js"></script>
<script src="jquery.cookie.js"></script>
<script src="myapp.js"></script>
Less HTTP requests
• After:
<script
src="all.js"
type="text/javascript">
</script>
Less HTTP requests
• You just saved 3 HTTP requests
Less HTTP requests
• repeat for CSS:
<link
href="all.css"
rel="stylesheet"
type="text/css”
/>
Less HTTP requests
• Inline images:
CSS sprites
with data: URI scheme
Less HTTP requests
• data: URI scheme
$ php ‐r "echo base64_encode(file_get_contents('my.png'));”
iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAIAAAA7ljmRAAAAGElEQVQIW2P4
DwcMDAxAfBvMAhEQMYgcACEHG8ELxtbPAAAAAElFTkSuQmCC
Less HTTP requests
• data: URI scheme
background‐image: url("data:image/png;base64,iVBORw0KG...");
Less HTTP requests
• data: URI scheme
<img src="data:image/png;base64,iVBOR..." />
Less HTTP requests
• data: URI scheme
• works in IE!...
Less HTTP requests
• data: URI scheme
• works in IE8!
Less HTTP requests
• data: URI scheme
• MHTML for IE < 8
http://www.phpied.com/mhtml-when-you-need-data-uris-in-ie7-and-under/
http://www.hedgerwow.com/360/dhtml/base64-image/demo.php
Less stuff? Cache
• Cache is less universal than
we think
• You can help
http://yuiblog.com/blog/2007/01/04/performance-research-part-2/
“never expire” policy
• Static components with far-
future Expires header
• JS, CSS, img
ExpiresActive On
ExpiresByType image/png "access plus 10 years"
Inline vs. external
• a.k.a. less http vs. more
cache
• how about both?
Inline vs. external
• First visit:
1. Inline
2. Lazy-load the external file
3. Write a cookie
Inline vs. external
• Later visits:
1. Read cookie
2. Refer to the external file
The Waterfall
1. Less stuff ✔
2. Smaller stuff
3. Out of the way
4. Start early
The Waterfall
1. Less stuff
2. Smaller stuff
3. Out of the way
4. Start early
Gzip
(function() {
var Y = YAHOO.util, // internal shorthand
getStyle, // for load time browser branching
setStyle, // ditto
propertyCache = {}, // for faster hyphen converts
reClassNameCache = {}, // cache regexes for className
document = window.document; // cache for faster lookups
YAHOO.env._id_counter = YAHOO.env._id_counter || 0;
Minify
• After
(function(){var
B=YAHOO.util,K,I,J={},F={},M=window.document;YAHOO.env._id_counter=YAHOO.en
v._id_counter||0;
Minify
• YUI Compressor
• Minifies JS and CSS
• Tolerates * and _ hacks
• More than minification
Minify
• Minify inline code too
Gzip or minification?
• 62,885 bytes - original jQuery (back in
Aug 2007)
• 31,822 - minified with the YUI
Compressor
• 19,758 - original gzipped
• 10,818 - minified and gzipped FTW
http://www.julienlecomte.net/blog/2007/08/13/
204
• The world’s smallest component?
• 204 No Content
<?php
header("HTTP/1.0 204 No Content");
// .... do your job, e.g. logging
?>
http://www.phpied.com/204-no-content/
The Waterfall
1. Less stuff ✔
2. Smaller stuff ✔
3. Out of the way
4. Start early
The Waterfall
1. Less stuff
2. Smaller stuff
3. Out of the way
4. Start early
Free-falling waterfalls
• Less DNS lookups – fetch
components from not more
than 2-4 domains
• Less redirects
• Blocking JavaScript
Not free-falling
JavaScript rocks!
• But also blocks
html
js
png
png
Non-blocking JavaScript
• Include via DOM
html
js
png
png
var js = document.createElement('script');
js.src = 'myscript.js';
var h = document.getElementsByTagName('head')[0];
h.appendChild(js);
Non-blocking JavaScript
• And what about my inline
scripts?
• Setup a collection (registry)
of inline scripts
Step 1
• Inline in the <head>:
var myapp = {
stuff: []
};
Step 2
• Add to the registry
Instead of:
<script>alert('boo!');</script>
Do:
<script>
myapp.stuff.push(function(){
alert('boo!');
});
</script>
Step 3
• Execute all
var l = myapp.stuff.length;
for(var i = 0, i < l; i++) {
myapp.stuff[i]();
}
Blocking CSS?
js
css
html
js
png
✔
css
flush()
<html>
<head>
<script src="my.js"
type="text/javascript"></script>
<link href="my.css"
type="text/css" rel="stylesheet" />
</head>
<?php flush() ?>
<body>
....
The Waterfall
1. Less stuff ✔
2. Smaller stuff ✔
3. Out of the way ✔
4. Start early ✔
Life after onload
Life after onload
1. Lazy-load
2. Preload
3. XHR
4. JavaScript optimizations
Lazy-load
var myApp = {
prop: huge
};
// ...
delete myApp.prop;
Cleaning up after yourself
• DOM elements you no longer
need
var el = $('mydiv');
el.parentNode.removeChild(el);
Cleaning up after yourself
• DOM elements you no longer
need
var el = $('mydiv');
delete el.parentNode.removeChild(el);
Init-time branching
• Instead of…
function myEvent(el, type, fn) {
if (window.addEventListener) {
el.addEventListener(type, fn, false);
} else if (window.attachEvent) {
el.attachEvent("on" + type, fn);
} else {…
}
Init-time branching
• Do…
if (window.addEventListener) {
var myEvent = function (el, type, fn) {
el.addEventListener(type, fn, false);
}
} else if (window.attachEvent) {
var myEvent = function (el, type, fn) {
el.attachEvent("on" + type, fn);
}
}
Lazy definition
function myEvent(el, type, fn) {
if (window.addEventListener) {
myEvent = function(el, type, fn) {
el.addEventListener(type, fn, false);
};
} else if (window.attachEvent) {
//...
}
return myEvent(el, type, fn);
}
Memoization
• for expensive, repeating tasks
function myFunc(param){
if (!myFunc.cache) {
myFunc.cache = {};
}
if (!myFunc.cache[param]) {
var result = {}; // …
myFunc.cache[param] = result;
}
return myFunc.cache[param];
}
Threads
• Web Workers for modern browsers
var myWorker = new Worker('my_worker.js');
myWorker.onmessage = function(event) {
alert("Called back by the worker!");
};
https://developer.mozilla.org/en/Using_DOM_workers
Threads
• … or setTimeout() for the rest
1. Do a chunk of work
2. setTimeout(chunk, 1) and return/yield
Life after onload
1. Lazy-load ✔
2. Preload ✔
3. XHR ✔
4. JavaScript optimizations ✔
YUI3
http://developer.yahoo.com/yui/3
YUI3
• Lighter
less KB, modules, sub-modules
• Faster
opportunity to refactor
• A la carte modules
YUI3 a la carte
• Combo handler
http://yui.yahooapis.com/combo?oop‐min.js&event‐min.js
• Self-populating
YUI().use(“anim”, function(Y) {
var a = new Y.Anim({...});
a.run();
});
Thank you!
Stoyan Stefanov
@stoyanstefanov
http://www.phpied.com
Credits/Further reading
• http://looksgoodworkswell.blogspot.com/2008/06/velocity-conference-improving-netflix.html
• http://developer.yahoo.com/yui/compressor/
• http://www.julienlecomte.net/blog/2007/12/39/
• http://webo.in/articles/habrahabr/46-cross-browser-data-url/
• http://yuiblog.com/blog/2008/07/22/non-blocking-scripts
• http://hitchhikers.wikia.com/wiki/Mostly_Harmless
• http://developer.yahoo.com/performance/
• http://oreilly.com/catalog/9780596522308/
• http://oreilly.com/catalog/9780596529307/
• http://www.nczonline.net/blog/tag/performance/