High Performance Kick Ass Web Apps (JavaScript edition)
-
Upload
stoyan-stefanov -
Category
Technology
-
view
47 -
download
2
description
Transcript of High Performance Kick Ass Web Apps (JavaScript edition)
High-Performance
Kick-Ass
Web Apps
(with focus on JavaScript)
Stoyan Stefanov, @stoyanstefanov April 25, 2009 JSConf, Washington, D.C.
About me • Yahoo! Search • Yahoo! Exceptional Performance • YSlow 2.0 architect • http://smush.it • Books, articles • http://phpied.com
Importance of performance
• 500 ms slower = 20% drop in traffic (Google)
Importance of performance
• 500 ms slower = 20% drop in traffic (Google) • 400 ms slower = 5-9% drop in full-page traffic (Yahoo!)
Importance of performance
• 500 ms slower = 20% drop in traffic (Google) • 400 ms slower = 5-9% drop in full-page traffic (Yahoo!) • 100 ms slower = 1% drop in sales (Amazon)
Importance of performance
• Self-regulating system • Slow down = lose users • It’s about user experience
“The premature optimization…
• … is the root of all evil” Knuth • “Make it right before you make it fast” Crockford
Pick your battles
• measure • profile • monitor
On trade-offs
“…everything has its drawbacks, as the man said when his mother-in-law died, and they came upon him for the funeral expenses.”
Jerome K. Jerome Three Man in a Boat
request HTML sent
onload page settles
conception birth graduation marriage? R.I.P.
User perceived “onload” happens somewhere here
request
The Life of Page 2.0
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'));” iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAIAAAA7ljmRAAAAGElEQVQIW2P4DwcMDAxAfBvMAhEQMYgcACEHG8ELxtbPAAAAAElFTkSuQmCC
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
Source: Bill Scott, Netflix
Minify
• Before /** * The dom module provides helper methods for * manipulating Dom elements. * @module dom * */
(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.env._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
http://www.julienlecomte.net/blog/2007/08/13/
FTW
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
var js = document.createElement('script'); js.src = 'myscript.js'; var h = document.getElementsByTagName('head')[0]; h.appendChild(js);
html
js
png
png
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?
But they do block: • In FF2 • When followed by a script
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
flush() early
html
png
js
css
html
png
js
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
• bells & whistles
• badges & widgets
Preload
• to help next page’s waterfall
• img, CSS, JS, DNS lookups
XHR (Ajax)
• small – gzip, JSON
• less – Expires
• GET over POST
GET vs. POST for XHR
var url = 'test.php';
var request = new XMLHttpRequest();
request.open("POST", url, false);
// …
request.send('test=1');
GET vs. POST for XHR
JavaScript optimizations
• local vars • DOM • garbage collection • init-time branching • memoization • threads
Local variables
• globals are all sorts of bad
• use var
• localize globals
Local variables
var a = 1; (function(){ var a = 2; function b(){ var a = 3; alert(a); } b(); })(); // 3
Local variables
var a = 1; (function(){ var a = 2; function b(){ // var a = 3; alert(a); } b(); })(); // 2
Local variables
var a = 1; (function(){ // var a = 2; function b(){ // var a = 3; alert(a); } b(); })(); // 1
Local variables
• less crawling up the scope chain
• localize
• function pointers too
• help YUI compressor (it won’t rename globals)
Wait! Isn’t that a
micro-optimization?
Localize DOM access
function foo(){
for (var i = 0; i < 100000; i++) {
document.getElementsByTagName('head');
}
}
foo();
Localize DOM access
function foo(){
var get = document.getElementsByTagName;
for (var i = 0; i < 100000; i++) {
get('head');
}
}
foo(); 4
times faster
Touching the DOM
function foo() {
for (var count = 0; count < 1000; count++) {
document.body.innerHTML += 1;
}
}
Touching the DOM
function foo() {
var inner = '';
for (var count = 0; count < 1000; count++) {
inner += 1;
}
document.body.innerHTML += inner;
} 1000 times faster
Cleaning up after yourself
• Properties you no longer need
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/