Even Faster Websites
Even Faster Websites
Even Faster Websites
Steve Souders
souders@google.com
http://stevesouders.com/docs/sxsw-20090314.ppt
Disclaimer: This content does not necessarily reflect the opinions of my employer.
the importance of frontend
performance
9% 91%
17% 83%
Yahoo!
Wikipedia
eBay
AOL
MySpace
YouTube
Facebook
scripts block
<script src="A.js"> blocks parallel
downloads and rendering
http://stevesouders.com/cuzillion/?ex=10008
MSN.com: parallel scripts
MSN
Scripts and other resources downloaded
in parallel! How? Secret sauce?!
var p=
g.getElementsByTagName("HEAD")[0];
var c=g.createElement("script");
c.type="text/javascript";
c.onreadystatechange=n;
c.onerror=c.onload=k;
c.src=e;
p.appendChild(c)
asynchronous script loading
XHR Eval
XHR Injection
Script in Iframe
Script DOM Element
Script Defer
document.write Script Tag
XHR Eval
var xhrObj = getXHRObject();
xhrObj.onreadystatechange =
function() {
if ( xhrObj.readyState != 4 ) return;
eval(xhrObj.responseText);
};
xhrObj.open('GET', 'A.js', true);
xhrObj.send('');
http://stevesouders.com/cuzillion/?ex=10012
Script DOM Element
var se = document.createElement('script');
se.src = 'http://anydomain.com/A.js';
document.getElementsByTagName('head')[0]
.appendChild(se);
http://stevesouders.com/cuzillion/?ex=10010
Script Defer
<script defer src='A.js'></script>
http://stevesouders.com/cuzillion/?ex=10013
document.write Script Tag
document.write("<scr" +
"ipt type='text/javascript' src='A.js'>" +
"</scr" + "ipt>");
http://stevesouders.com/cuzillion/?ex=10014
browser busy indicators
browser busy indicators
*
Only other document.write scripts are downloaded in parallel (in the same script block).
and the winner is...
XHR Eval
XHR Injection
Script in iframe
Script DOM
Element
Script Defer
same domains
different domains
after
load scripts without blocking
!IE
*
Only other document.write scripts are downloaded in parallel (in the same script block).
asynchronous scripts wrap-up
what about
inlined code
that depends on the script?
what about
inlined code
that depends on the script?
baseline coupling results (not good)
hardcoded callback
window onload
timer
degrading script tags
script onload
technique 1: hardcoded callback
<script type="text/javascript">
var aExamples = [['couple-normal.php', 'Normal Script Src'],
...];
function init() {
EFWS.Menu.createMenu('examplesbtn', aExamples);
}
var domscript = document.createElement('script');
domscript.src = "menu.js";
document.getElementsByTagName('head')[0].appendChild(domscri
pt);
</script>
two solutions:
− Managed XHR
− DOM Element and Doc Write
multiple script example: menutier.js
<script src="menu.js" type="text/javascript"></script>
<script src="menutier.js" type="text/javascript"></script>
<script type="text/javascript">
var aRaceConditions = [['couple-normal.php', 'Normal...];
var aWorkarounds = [['hardcoded-callback.php', 'Hardcod...];
var aMultipleScripts = [['managed-xhr.php', 'Managed XH...];
var aLoadScripts = [['loadscript.php', 'loadScript'], ...];
var aSubmenus =
[["Race Conditions", aRaceConditions],
["Workarounds", aWorkarounds],
["Multiple Scripts", aMultipleScripts],
["General Solution", aLoadScripts]];
function init() {
EFWS.Menu.createTieredMenu('examplesbtn', aSubmenus);
}
</script>
technique 1: managed XHR
<script type="text/javascript">
var aRaceConditions = [['couple-normal.php', 'Normal...];
var aWorkarounds = [['hardcoded-callback.php', 'Hardcod...];
var aMultipleScripts = [['managed-xhr.php', 'Managed XH...];
var aLoadScripts = [['loadscript.php', 'loadScript'], ...];
var aSubmenus = [["Race Conditions", aRaceConditions], ...];
function init() {
EFWS.Menu.createTieredMenu('examplesbtn', aSubmenus);
}
before
after
recommended pattern: 1
<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ?
"https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost +
"google-analytics.com/ga.js'
type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-xxxxxx-x");
pageTracker._trackPageview();
</script>
_loadGA: function(){
var gaHost = ("https:" == document.location.protocol) ?
"https://ssl." : "http://www.";
dojo.create('script', {
src: gaHost + "google-analytics.com/ga.js"
}, dojo.doc.getElementsByTagName("head")[0]);
setTimeout(dojo.hitch(this, "_checkGA"), this.loadInterval);
},
_checkGA: function(){
setTimeout(dojo.hitch(this, !window["_gat"] ? "_checkGA" :
"_gotGA"), this.loadInterval);
},
_gotGA: function(){
this.tracker = _gat._getTracker(this.acct); ...
}
MSN
Wikipedia
eBay
MySpace
iframes:
most expensive DOM element
load 100 empty elements
of each type
tested in all major
browsers 1
1
IE 6, 7, 8; FF 2, 3.0, 3.1b2; Safari 3.2, 4; Opera 9.63, 10; Chrome 1.0, 2.0
iframes block onload
Firefox script
Safari script
Chrome
Opera
Firefox stylesheet
Safari stylesheet
Chrome
Opera
Firefox
stylesheet
Safari
Chrome stylesheet
Opera
iframe
html
image
image
script
call PHP's flush()
gotchas:
– PHP output_buffering – ob_flush()
– Transfer-Encoding: chunked
– gzip – Apache's DeflateBufferSize before 2.2.8
– proxies and anti-virus software
– browsers – Safari (1K), Chrome (2K)
other languages:
$| or FileHandle autoflush (Perl), flush
(Python), ios.flush (Ruby)
flushing and domain blocking
html
image
image different domains
script
1
http://home.blarg.net/~glinden/StanfordDataMining.2006-11-29.ppt
2
http://www.slideshare.net/stoyan/yslow-20-presentation
cost savings
http://billwscott.com/share/presentations/2008/stanford/HPWP-RealWorld.pdf
if you want
better user experience
more revenue
reduced operating
expenses
the strategy is clear