Download presentation
Presentation is loading. Please wait.
Published byMegan Black Modified over 11 years ago
1
Steve Souders souders@google.com http://stevesouders.com/docs/google-20090305.ppt Even Faster Web Sites Disclaimer: This content does not necessarily reflect the opinions of my employer.
2
17% 83% iGoogle, primed cache The Importance of Frontend Performance 9%91% iGoogle, empty cache
3
Time Spent on the Frontend Empty CachePrimed Cache www.aol.com97% www.ebay.com95%81% www.facebook.com95%81% www.google.com/search47%0% search.live.com/results67%0% www.msn.com98%94% www.myspace.com98% en.wikipedia.org/wiki94%91% www.yahoo.com97%96% www.youtube.com98%97% April 2008
4
14 Rules 1.Make fewer HTTP requests 2.Use a CDN 3.Add an Expires header 4.Gzip components 5.Put stylesheets at the top 6.Put scripts at the bottom 7.Avoid CSS expressions 8.Make JS and CSS external 9.Reduce DNS lookups 10.Minify JS 11.Avoid redirects 12.Remove duplicate scripts 13.Configure ETags 14.Make AJAX cacheable
10
Even Faster Web Sites Split the initial payload Load scripts without blocking Couple asynchronous scripts Don't scatter inline scripts Split the dominant domain Flush the document early Use iframes sparingly Simplify CSS Selectors O'Reilly, June 2009 Ajax performance (Doug Crockford) Writing efficient JavaScript (Nicholas Zakas) Creating responsive web apps (Ben Galbraith, Dion Almaer) Comet (Dylan Schiemann) Beyond Gzipping (Tony Gentilcore) Optimize Images (Stoyan Stefanov, Nicole Sullivan)
11
AOL eBay Facebook MySpace Wikipedia Yahoo! Why focus on JavaScript? YouTube
12
Scripts Block blocks parallel downloads and rendering http://stevesouders.com/cuzillion/?ex=10008
13
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) MSN.com: Parallel Scripts
14
Advanced Script Loading XHR Eval XHR Injection Script in Iframe Script DOM Element Script Defer document.write Script Tag
15
XHR Eval script must have same domain as main page must refactor script 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=10009
16
XHR Injection var xhrObj = getXHRObject(); xhrObj.onreadystatechange = function() { if ( xhrObj.readyState != 4 ) return; var se=document.createElement('script'); document.getElementsByTagName('head') [0].appendChild(se); se.text = xhrObj.responseText; }; xhrObj.open('GET', 'A.js', true); xhrObj.send(''); script must have same domain as main page http://stevesouders.com/cuzillion/?ex=10015
17
Script in Iframe <iframe src='A.html' width=0 height=0 frameborder=0 id=frame1> iframe must have same domain as main page must refactor script: // access iframe from main page window.frames[0].createNewDiv(); // access main page from iframe parent.document.createElement('div'); http://stevesouders.com/cuzillion/?ex=10012
18
Script DOM Element var se = document.createElement('script'); se.src = 'http://anydomain.com/A.js'; document.getElementsByTagName('head') [0].appendChild(se); script and main page domains can differ no need to refactor JavaScript http://stevesouders.com/cuzillion/?ex=10010
19
Script Defer only supported in IE (just landed in FF 3.1) script and main page domains can differ no need to refactor JavaScript http://stevesouders.com/cuzillion/?ex=10013
20
document.write Script Tag document.write("<scr" + "ipt type='text/javascript' src='A.js'>" + " "); parallelization only works in IE parallel downloads for scripts, nothing else all document.write s must be in same script block http://stevesouders.com/cuzillion/?ex=10014
21
Browser Busy Indicators
22
status bar progress bar logocursor block render block onload normal Script Src FFIE,FF FFIE,FF XHR Eval no XHR Injection no Script in Iframe IE,FFFFIE,FFFFnoIE,FF Script DOM Element FF noFF Script Defer FF IE,FF document.write Script Tag FFIE,FF FFIE,FF good to show busy indicators when the user needs feedback bad when downloading in the background
23
Ensure scripts execute in order: necessary when scripts have dependencies IE: http://stevesouders.com/cuzillion/?ex=10017 http://stevesouders.com/cuzillion/?ex=10017 FF: http://stevesouders.com/cuzillion/?ex=10018 http://stevesouders.com/cuzillion/?ex=10018 Avoid scripts executing in order: faster – first script back is executed immediately http://stevesouders.com/cuzillion/?ex=10019 Ensure/Avoid Ordered Execution
24
Summary of Traits || down- loads domains can differ existing scripts browser busy ensures order size (bytes) normal Script Src noyes IE,FF ~50 XHR Eval IE,FFno ~500 XHR Injection IE,FFnoyesno ~500 Script in Iframe IE,FFno IE,FFno~50 Script DOM Element IE,FFyes FF ~200 Script Defer IEyes IE,FFIE~50 document.write Script Tag IE * yes IE,FFIE~100 * Only other document.write scripts are downloaded in parallel (in the same script block).
25
and the winner is... XHR Eval XHR Injection Script in iframe Script DOM Element Script Defer Script DOM Element Script Defer Script DOM Element Script DOM Element (FF) Script Defer (IE) XHR Eval XHR Injection Script in iframe Script DOM Element (IE) XHR Injection XHR Eval Script DOM Element (IE) Managed XHR Injection Managed XHR Eval Script DOM Element Managed XHR Injection Managed XHR Eval Script DOM Element (FF) Script Defer (IE) Managed XHR Eval Managed XHR Injection Script DOM Element (FF) Script Defer (IE) Managed XHR Eval Managed XHR Injection different domains same domains no order preserve orderno order no busy show busy no busy preserve order
26
Load Scripts without Blocking don't let scripts block other downloads you can still control execution order, busy indicators, and onload event What about inline scripts?
27
what about inlined code that depends on the script?
29
synchronous JS example: menu.js var aExamples = [ ['couple-normal.php', 'Normal Script Src'], ['couple-xhr-eval.php', 'XHR Eval'],... ['managed-xhr.php', 'Managed XHR'] ]; function init() { EFWS.Menu.createMenu('examplesbtn', aExamples); } init();
30
asynchronous JS example: menu.js var domscript = document.createElement('script'); domscript.src = "menu.js"; document.getElementsByTagName('head')[0].appendChild(domscri pt); var aExamples = [ ['couple-normal.php', 'Normal Script Src'], ['couple-xhr-eval.php', 'XHR Eval'],... ['managed-xhr.php', 'Managed XHR'] ]; function init() { EFWS.Menu.createMenu('examplesbtn', aExamples); } init(); before after
32
baseline coupling results (not good) Preserve Execution Order Load Script & Image in Parallel normal Script Src allIE8, Saf4, Chr2 XHR Eval -all XHR Injection -all Script in Iframe -all Script DOM Element FF, OpIE, FF, Saf, Chr Script Defer FF, Saf, Chr, OpIE, (Saf4, Chr2) * document.write Script Tag allSaf4, Chr2 * Scripts download in parallel regardless of the Defer attribute. need a way to load scripts asynchronously AND preserve order
33
coupling techniques hardcoded callback window onload timer script onload degrading script tags
34
technique 1: hardcoded callback 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); init() is called from within menu.js not very flexible doesn't work for 3 rd party scripts
35
technique 2: window onload var aExamples = [['couple-normal.php', 'Normal Script Src'],...]; function init() { EFWS.Menu.createMenu('examplesbtn', aExamples); } if ( window.addEventListener ) { window.addEventListener("load", init, false); } else if ( window.attachEvent ) { window.attachEvent("onload", init); } init() is called at window onload must use async technique that blocks onload: Script in Iframe does this across most browsers init() called later than necessary
36
technique 3: timer var domscript = document.createElement('script'); domscript.src = "menu.js"; document.getElementsByTagName('head')[0].appendChild(domscript); var aExamples = [['couple-normal.php', 'Normal Script Src'],...]; function init() { EFWS.Menu.createMenu('examplesbtn', aExamples); } function initTimer(interval) { if ( "undefined" === typeof(EFWS) ) { setTimeout(initTimer, interval); } else { init(); } initTimer(300); load if interval too low, delay if too high slight increased maintenance – EFWS
37
technique 4: script onload var aExamples = [['couple-normal.php', 'Normal Script Src'],...]; function init() { EFWS.Menu.createMenu('examplesbtn', aExamples); } var domscript = document.createElement('script'); domscript.src = "menu.js"; domscript.onloadDone = false; domscript.onload = function() { domscript.onloadDone = true; init(); }; domscript.onreadystatechange = function() { if ( "loaded" === domscript.readyState && ! domscript.onloadDone ) { domscript.onloadDone = true; init(); } document.getElementsByTagName('head')[0].appendChild(domscript); pretty nice, more complicated
38
John Resig's degrading script tags var aExamples = [['couple-normal.php', 'Normal Script Src'],...]; function init() { EFWS.Menu.createMenu('examplesbtn', aExamples); } init(); cleaner clearer safer – inlined code not called if script fails at the end of menu-degrading.js: var scripts = document.getElementsByTagName("script"); var cntr = scripts.length; while ( cntr ) { var curScript = scripts[cntr-1]; if (curScript.src.indexOf("menu-degrading.js") != -1) { eval( curScript.innerHTML ); break; } cntr--; } http://ejohn.org/blog/degrading-script-tags/
39
technique 5: degrading script tags var aExamples = [['couple-normal.php', 'Normal Script Src'],...]; function init() { EFWS.Menu.createMenu('examplesbtn', aExamples); } var domscript = document.createElement('script'); domscript.src = "menu-degrading.js"; if ( -1 != navigator.userAgent.indexOf("Opera") ) { domscript.innerHTML = "init();"; } else { domscript.text = "init();"; } document.getElementsByTagName('head')[0].appendChild(domscript); elegant, flexible (cool!) not well known doesn't work for 3 rd party scripts (unless...)
40
what about multiple scripts that depend on each other, and inlined code that depends on the scripts? two solutions: Managed XHR DOM Element and Doc Write
41
multiple script example: menutier.js 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); }
42
technique 1: managed XHR 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); } EFWS.Script.loadScriptXhrInjection("menu.js", null, true); EFWS.Script.loadScriptXhrInjection("menutier.js", init, true); XHR Injection asynchronous technique does not preserve order – we have to add that before after
43
EFWS.loadScriptXhrInjection // Load an external script. // Optionally call a callback and preserve order. loadScriptXhrInjection: function(url, onload, bOrder) { var iQ = EFWS.Script.queuedScripts.length; if ( bOrder ) { var qScript = { response: null, onload: onload, done: false }; EFWS.Script.queuedScripts[iQ] = qScript; } var xhrObj = EFWS.Script.getXHRObject(); xhrObj.onreadystatechange = function() { if ( xhrObj.readyState == 4 ) { if ( bOrder ) { EFWS.Script.queuedScripts[iQ].response = xhrObj.responseText; EFWS.Script.injectScripts(); } else { eval(xhrObj.responseText); if ( onload ) { onload(); } }; xhrObj.open('GET', url, true); xhrObj.send(''); } process queue (next slide)or... eval now, call callback save response to queue add to queue (if bOrder)
44
EFWS.injectScripts // Process queued scripts to see if any are ready to inject. injectScripts: function() { var len = EFWS.Script.queuedScripts.length; for ( var i = 0; i < len; i++ ) { var qScript = EFWS.Script.queuedScripts[i]; if ( ! qScript.done ) { if ( ! qScript.response ) { // STOP! need to wait for this response break; } else { eval(qScript.response); if ( qScript.onload ) { qScript.onload(); } qScript.done = true; } ready for this script, eval and call callback bail – need to wait to preserve order if not yet injected preserves external script ordernon-blocking works in all browserscouples with inlined code works with scripts across domains
45
technique 2: DOM Element and Doc Write Preserve Execution Order Load Scripts in Parallel Load Script & Image in Parallel Script DOM Element FF, Op FF, Op, IE, Saf, Chr FF, IE, Saf, Chr Script Defer IE, Saf, Chr, FF, Op IE document.write Script Tag IE, Saf, Chr, FF, Op IE, Op Firefox & Opera – use Script DOM Element IE – use document.write Script Tag Safari, Chrome – no benefit; rely on Safari 4 and Chrome 2
46
EFWS.loadScripts loadScripts: function(aUrls, onload) { // first pass: see if any of the scripts are on a different domain var nUrls = aUrls.length; var bDifferent = false; for ( var i = 0; i < nUrls; i++ ) { if ( EFWS.Script.differentDomain(aUrls[i]) ) { bDifferent = true; break; } // pick the best loading function var loadFunc = EFWS.Script.loadScriptXhrInjection; if ( bDifferent ) { if ( -1 != navigator.userAgent.indexOf('Firefox') || -1 != navigator.userAgent.indexOf('Opera') ) { loadFunc = EFWS.Script.loadScriptDomElement; } else { loadFunc = EFWS.Script.loadScriptDocWrite; } // second pass: load the scripts for ( var i = 0; i < nUrls; i++ ) { loadFunc(aUrls[i], ( i+1 == nUrls ? onload : null ), true); }
47
multiple scripts with dependencies 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); } EFWS.Script.loadScripts(["menu.js", "menutier.js"], init); scripts on same domain: order preserved, no blocking scripts on different domain: order preserved: all loads scripts in parallel: all except Saf3, Chr1 load script and image in parallel: FF, Saf4, Chr2
48
coupling wrap-up Technique Preserve Order Load Scripts in Parallel Load Script & Image in Parallel single script Script DOM Element na all multiple scripts, no dependencies Script DOM Element naall multiple scripts, dependencies, same domain Managed XHRall multiple scripts, dependencies, same domain Script DOM Element (FF, Op), Doc Write (IE, Saf, Chr) all !Saf3, !Chr1 FF, Saf4, Chr2
49
case study: Google Analytics recommended pattern: 1 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")); var pageTracker = _gat._getTracker("UA-xxxxxx-x"); pageTracker._trackPageview(); document.write Script Tag approach blocks other resources 1 http://www.google.com/support/analytics/bin/answer.py?hl=en&answer=55488
50
case study: Google Analytics dojox.analytics.Urchin: 1 _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);... } Script DOM Element approach timer coupling technique (script onload better) 1 http://docs.dojocampus.org/dojox/analytics/Urchin
51
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
52
iframes block onload parent's onload doesn't fire until iframe and all its components are downloaded workaround for Safari and Chrome: set iframe src in JavaScript document.getElementById('iframe1').src="url";
53
scripts block iframe no surprise – scripts in the parent block the iframe from loading IE Firefox Safari Chrome Opera script
54
stylesheets block iframe (IE, FF) surprise – stylesheets in the parent block the iframe or its resources in IE & Firefox IE Firefox Safari Chrome Opera stylesheet
55
stylesheets after iframe still block (FF) surprise – even moving the stylesheet after the iframe still causes the iframe's resources to be blocked in Firefox IE Firefox Safari Chrome Opera stylesheet
56
iframes: no free connections iframe shares connection pool with parent (here – 2 connections per server in IE 7) iframe parent
57
flush the document early 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) html image script html image script
58
flushing and domain blocking you might need to move flushed resources to a domain different from the HTML doc html image script html image script google image script image 204
59
Takeaways focus on the frontend run YSlow: http://developer.yahoo.com/yslow http://developer.yahoo.com/yslow this year's focus: JavaScript Split the Initial Payload Load Scripts without Blocking Don't Scatter Inline Scripts speed matters
60
Impact on Revenue Google: Yahoo: Amazon: 1 http://home.blarg.net/~glinden/StanfordDataMining.2006-11-29.ppt 2 http://www.slideshare.net/stoyan/yslow-20-presentation +500 ms -20% traffic 1 +400 ms -5-9% full-page traffic 2 +100 ms -1% sales 1
61
Cost Savings hardware – reduced load bandwidth – reduced response size http://billwscott.com/share/presentations/2008/stanford/HPWP-RealWorld.pdf
62
if you want better user experience more revenue reduced operating expenses the strategy is clear Even Faster Web Sites
63
Steve Souders souders@google.com http://stevesouders.com/docs/google-20090305.ppt
Similar presentations
© 2024 SlidePlayer.com. Inc.
All rights reserved.