0
Steve Souders [email_address] http://stevesouders.com/docs/sxsw-20090314.ppt Even Faster Web Sites Disclaimer:  This conte...
the importance of frontend performance 17% 83% iGoogle, primed cache 9% 91% iGoogle, empty cache
time spent on the frontend April 2008 Empty Cache Primed Cache www.aol.com 97% 97% www.ebay.com 95% 81% www.facebook.com 9...
14 RULES <ul><li>MAKE FEWER HTTP REQUESTS </li></ul><ul><li>USE A CDN </li></ul><ul><li>ADD AN EXPIRES HEADER </li></ul><u...
 
 
 
25% discount code: &quot;ssouders25&quot;
Sept 2007
June 2009
Even Faster Websites Split the initial payload Load scripts without blocking Couple asynchronous scripts Don't scatter inl...
why focus on JavaScript? AOL eBay Facebook MySpace Wikipedia Yahoo! YouTube
scripts block <script src=&quot;A.js&quot;>  blocks parallel downloads and rendering http://stevesouders.com/cuzillion/?ex...
MSN.com: parallel scripts Scripts and other resources downloaded in parallel! How? Secret sauce?! var p= g.getElementsByTa...
asynchronous script loading XHR Eval XHR Injection Script in Iframe Script DOM Element Script Defer document.write Script ...
XHR Eval script must have same domain as main page must refactor script var xhrObj = getXHRObject(); xhrObj.onreadystatech...
XHR Injection var xhrObj = getXHRObject(); xhrObj.onreadystatechange =  function() {  if ( xhrObj.readyState != 4 ) return...
Script in Iframe <iframe  src='A.html'  width=0 height=0  frameborder=0 id=frame1></iframe>  <ul><li>iframe must have same...
Script DOM Element var se = document.createElement('script'); se.src = 'http://anydomain.com/A.js'; document.getElementsBy...
Script Defer <script  defer  src='A.js'></script> only supported in IE (just landed in FF 3.1) script and main page domain...
document.write  Script Tag document.write (&quot;<scr&quot; +  &quot;ipt type='text/javascript' src='A.js'>&quot; + &quot;...
browser busy indicators
browser busy indicators good  to show busy indicators when the user needs feedback bad  when downloading in the background
ensure/avoid ordered execution <ul><li>Ensure scripts execute in order: </li></ul><ul><ul><li>necessary when scripts have ...
load scripts without blocking * 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 Script DOM Element Script Def...
load scripts without blocking <ul><li>don't let scripts block other downloads </li></ul><ul><li>you can still control exec...
 
synchronous JS example: menu.js <ul><li><script src=&quot;menu.js&quot; type=&quot;text/javascript&quot;></script> </li></...
asynchronous JS example: menu.js <ul><li><script type=&quot;text/javascript&quot;> </li></ul><ul><li>var domscript = docum...
before after
load scripts without blocking * Only other document.write scripts are downloaded in parallel (in the same script block). !IE
asynchronous scripts wrap-up <ul><li>what about </li></ul><ul><li>inlined code  </li></ul><ul><li>that depends on the scri...
<ul><li>what about </li></ul><ul><li>inlined code  </li></ul><ul><li>that depends on the script? </li></ul>
baseline coupling results (not good) *  Scripts download in parallel regardless of the Defer attribute. need a way to load...
coupling techniques <ul><li>hardcoded callback </li></ul><ul><li>window onload </li></ul><ul><li>timer </li></ul><ul><li>d...
technique 1: hardcoded callback <ul><li><script type=&quot;text/javascript&quot;> </li></ul><ul><li>var aExamples = [['cou...
technique 2: window onload <ul><li><iframe src=&quot;menu.php&quot; width=0 height=0 frameborder=0> </iframe> </li></ul><u...
technique 3: timer <ul><li><script type=&quot;text/javascript&quot;> </li></ul><ul><li>var domscript = document.createElem...
John Resig's degrading script tags <ul><li><script src=&quot;menu-degrading.js&quot; type=&quot;text/javascript&quot;> </l...
technique 4: degrading script tags <ul><li><script type=&quot;text/javascript&quot;> </li></ul><ul><li>var aExamples = [['...
technique 5: script onload <ul><li><script type=&quot;text/javascript&quot;> </li></ul><ul><li>var aExamples = [['couple-n...
<ul><li>what about </li></ul><ul><li>multiple scripts  </li></ul><ul><li>that depend on each other,  </li></ul><ul><li>and...
multiple script example: menutier.js <ul><li><script src=&quot;menu.js&quot; type=&quot;text/javascript&quot;></script> </...
technique 1: managed XHR <ul><li><script type=&quot;text/javascript&quot;> </li></ul><ul><li>var aRaceConditions = [['coup...
EFWS.loadScriptXhrInjection <ul><li>// Load an external script.  </li></ul><ul><li>// Optionally call a callback and prese...
EFWS.injectScripts <ul><li>// Process queued scripts to see if any are ready to inject. </li></ul><ul><li>injectScripts: f...
technique 2: DOM Element and Doc Write Firefox & Opera – use Script DOM Element IE – use  document.write  Script Tag Safar...
EFWS.loadScripts <ul><li>loadScripts: function(aUrls, onload) { </li></ul><ul><li>// first pass: see if any of the scripts...
multiple scripts with dependencies <ul><li><script type=&quot;text/javascript&quot;> </li></ul><ul><li>var aRaceConditions...
asynchronous scripts wrap-up
case study: Google Analytics <ul><li>recommended pattern: 1 </li></ul><ul><li><script type=&quot;text/javascript&quot;> </...
case study: dojox.analytics.Urchin 1 <ul><li>_loadGA: function(){ </li></ul><ul><li>var gaHost = (&quot;https:&quot; == do...
asynchronous loading & coupling <ul><li>async technique: Script DOM Element </li></ul><ul><ul><li>easy, cross-browser </li...
bad: stylesheet followed by inline script <ul><li>browsers download stylesheets in parallel with other resources that foll...
don't scatter inline scripts eBay MSN MySpace Wikipedia
iframes:    most expensive DOM element <ul><li>load 100  empty  elements of each type </li></ul><ul><li>tested in all majo...
iframes block onload <ul><li>parent's onload doesn't fire until iframe and all its components are downloaded </li></ul><ul...
scripts block iframe <ul><li>no surprise – scripts in the parent block the iframe from loading </li></ul>IE Firefox Safari...
stylesheets block iframe (IE, FF) <ul><li>surprise – stylesheets in the parent block the iframe or its resources in IE & F...
stylesheets after iframe still block (FF) <ul><li>surprise – even moving the stylesheet  after  the iframe still causes th...
iframes: no free connections <ul><li>iframe shares connection pool with parent (here – 2 connections per server in IE 7) <...
flush the document early <ul><li>gotchas: </li></ul><ul><ul><li>PHP output_buffering –  ob_flush() </li></ul></ul><ul><ul>...
flushing and domain blocking <ul><li>you might need to move flushed resources to a domain different from the HTML doc </li...
takeaways <ul><li>focus on the frontend </li></ul><ul><li>run YSlow:  http://developer.yahoo.com/yslow </li></ul><ul><li>t...
impact on revenue <ul><li>Google: </li></ul><ul><li>Yahoo: </li></ul><ul><li>Amazon: </li></ul>1  http://home.blarg.net/~g...
cost savings <ul><li>hardware – reduced load </li></ul><ul><li>bandwidth – reduced response size </li></ul>http://billwsco...
<ul><li>if you want </li></ul><ul><ul><li>better user experience </li></ul></ul><ul><ul><li>more revenue </li></ul></ul><u...
Steve Souders [email_address] http://stevesouders.com/docs/sxsw-20090314.ppt
Upcoming SlideShare
Loading in...5
×

SXSW: Even Faster Web Sites

10,613

Published on

Presented at SXSW '09, this talk covers five best practices from my next book: Load scripts without blocking, Coupling asynchronous scripts, Don't scatter inline scripts, Use iframes sparingly, and Flush the document early.

Published in: Education, Technology
2 Comments
57 Likes
Statistics
Notes
No Downloads
Views
Total Views
10,613
On Slideshare
0
From Embeds
0
Number of Embeds
6
Actions
Shares
0
Downloads
323
Comments
2
Likes
57
Embeds 0
No embeds

No notes for slide
  • Permission to use photo given by Amnemona: http://www.flickr.com/photos/marinacvinhal/379111290/
  • Transcript of "SXSW: Even Faster Web Sites"

    1. 1. Steve Souders [email_address] http://stevesouders.com/docs/sxsw-20090314.ppt Even Faster Web Sites Disclaimer: This content does not necessarily reflect the opinions of my employer.
    2. 2. the importance of frontend performance 17% 83% iGoogle, primed cache 9% 91% iGoogle, empty cache
    3. 3. time spent on the frontend April 2008 Empty Cache Primed Cache www.aol.com 97% 97% www.ebay.com 95% 81% www.facebook.com 95% 81% www.google.com/search 47% 0% search.live.com/results 67% 0% www.msn.com 98% 94% www.myspace.com 98% 98% en.wikipedia.org/wiki 94% 91% www.yahoo.com 97% 96% www.youtube.com 98% 97%
    4. 4. 14 RULES <ul><li>MAKE FEWER HTTP REQUESTS </li></ul><ul><li>USE A CDN </li></ul><ul><li>ADD AN EXPIRES HEADER </li></ul><ul><li>GZIP COMPONENTS </li></ul><ul><li>PUT STYLESHEETS AT THE TOP </li></ul><ul><li>PUT SCRIPTS AT THE BOTTOM </li></ul><ul><li>AVOID CSS EXPRESSIONS </li></ul><ul><li>MAKE JS AND CSS EXTERNAL </li></ul><ul><li>REDUCE DNS LOOKUPS </li></ul><ul><li>MINIFY JS </li></ul><ul><li>AVOID REDIRECTS </li></ul><ul><li>REMOVE DUPLICATE SCRIPTS </li></ul><ul><li>CONFIGURE ETAGS </li></ul><ul><li>MAKE AJAX CACHEABLE </li></ul>
    5. 8. 25% discount code: &quot;ssouders25&quot;
    6. 9. Sept 2007
    7. 10. June 2009
    8. 11. Even Faster Websites 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 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)
    9. 12. why focus on JavaScript? AOL eBay Facebook MySpace Wikipedia Yahoo! YouTube
    10. 13. scripts block <script src=&quot;A.js&quot;> blocks parallel downloads and rendering http://stevesouders.com/cuzillion/?ex=10008
    11. 14. MSN.com: parallel scripts Scripts and other resources downloaded in parallel! How? Secret sauce?! var p= g.getElementsByTagName(&quot;HEAD&quot;)[0]; var c=g.createElement(&quot;script&quot;); c.type=&quot;text/javascript&quot;; c.onreadystatechange=n; c.onerror=c.onload=k; c.src=e; p.appendChild(c) MSN
    12. 15. asynchronous script loading XHR Eval XHR Injection Script in Iframe Script DOM Element Script Defer document.write Script Tag
    13. 16. 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
    14. 17. 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
    15. 18. Script in Iframe <iframe src='A.html' width=0 height=0 frameborder=0 id=frame1></iframe> <ul><li>iframe must have same domain as main page </li></ul><ul><li>must refactor script: </li></ul><ul><ul><li>// access iframe from main page </li></ul></ul><ul><ul><li>window.frames[0].createNewDiv(); </li></ul></ul><ul><ul><li>// access main page from iframe </li></ul></ul><ul><ul><li>parent.document.createElement('div'); </li></ul></ul>http://stevesouders.com/cuzillion/?ex=10012
    16. 19. 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
    17. 20. Script Defer <script defer src='A.js'></script> 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
    18. 21. document.write Script Tag document.write (&quot;<scr&quot; + &quot;ipt type='text/javascript' src='A.js'>&quot; + &quot;</scr&quot; + &quot;ipt>&quot;); 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
    19. 22. browser busy indicators
    20. 23. browser busy indicators good to show busy indicators when the user needs feedback bad when downloading in the background
    21. 24. ensure/avoid ordered execution <ul><li>Ensure scripts execute in order: </li></ul><ul><ul><li>necessary when scripts have dependencies </li></ul></ul><ul><ul><li>IE: http://stevesouders.com/cuzillion/?ex=10017 </li></ul></ul><ul><ul><li>FF: http://stevesouders.com/cuzillion/?ex=10018 </li></ul></ul><ul><li>Avoid scripts executing in order: </li></ul><ul><ul><li>faster – first script back is executed immediately </li></ul></ul><ul><ul><li>http://stevesouders.com/cuzillion/?ex=10019 </li></ul></ul>
    22. 25. load scripts without blocking * Only other document.write scripts are downloaded in parallel (in the same script block).
    23. 26. 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 order no order no busy show busy show busy no busy preserve order
    24. 27. load scripts without blocking <ul><li>don't let scripts block other downloads </li></ul><ul><li>you can still control execution order, busy indicators, and onload event </li></ul><ul><li>What about inline scripts? </li></ul>
    25. 29. synchronous JS example: menu.js <ul><li><script src=&quot;menu.js&quot; type=&quot;text/javascript&quot;></script> </li></ul><ul><li><script type=&quot;text/javascript&quot;> </li></ul><ul><li>var aExamples = </li></ul><ul><li>[ </li></ul><ul><li>['couple-normal.php', 'Normal Script Src'], </li></ul><ul><li>['couple-xhr-eval.php', 'XHR Eval'], </li></ul><ul><li>... </li></ul><ul><li>['managed-xhr.php', 'Managed XHR'] </li></ul><ul><li>]; </li></ul><ul><li>function init() { </li></ul><ul><li>EFWS.Menu.createMenu('examplesbtn', aExamples); </li></ul><ul><li>} </li></ul><ul><li>init(); </li></ul><ul><li></script> </li></ul>
    26. 30. asynchronous JS example: menu.js <ul><li><script type=&quot;text/javascript&quot;> </li></ul><ul><li>var domscript = document.createElement('script'); </li></ul><ul><li>domscript.src = &quot;menu.js&quot;; </li></ul><ul><li>document.getElementsByTagName('head')[0].appendChild(domscript); </li></ul><ul><li>var aExamples = </li></ul><ul><li>[ </li></ul><ul><li>['couple-normal.php', 'Normal Script Src'], </li></ul><ul><li>['couple-xhr-eval.php', 'XHR Eval'], </li></ul><ul><li>... </li></ul><ul><li>['managed-xhr.php', 'Managed XHR'] </li></ul><ul><li>]; </li></ul><ul><li>function init() { </li></ul><ul><li>EFWS.Menu.createMenu('examplesbtn', aExamples); </li></ul><ul><li>} </li></ul><ul><li>init(); </li></ul><ul><li></script> </li></ul>script DOM element approach
    27. 31. before after
    28. 32. load scripts without blocking * Only other document.write scripts are downloaded in parallel (in the same script block). !IE
    29. 33. asynchronous scripts wrap-up <ul><li>what about </li></ul><ul><li>inlined code </li></ul><ul><li>that depends on the script? </li></ul>
    30. 34. <ul><li>what about </li></ul><ul><li>inlined code </li></ul><ul><li>that depends on the script? </li></ul>
    31. 35. baseline coupling results (not good) * Scripts download in parallel regardless of the Defer attribute. need a way to load scripts asynchronously AND preserve order
    32. 36. coupling techniques <ul><li>hardcoded callback </li></ul><ul><li>window onload </li></ul><ul><li>timer </li></ul><ul><li>degrading script tags </li></ul><ul><li>script onload </li></ul>
    33. 37. technique 1: hardcoded callback <ul><li><script type=&quot;text/javascript&quot;> </li></ul><ul><li>var aExamples = [['couple-normal.php', 'Normal Script Src'], ...]; </li></ul><ul><li>function init() { </li></ul><ul><li>EFWS.Menu.createMenu('examplesbtn', aExamples); </li></ul><ul><li>} </li></ul><ul><li>var domscript = document.createElement('script'); </li></ul><ul><li>domscript.src = &quot;menu.js&quot;; </li></ul><ul><li>document.getElementsByTagName('head')[0].appendChild(domscript); </li></ul><ul><li></script> </li></ul><ul><li>init() is called from within menu.js </li></ul><ul><li>not very flexible </li></ul><ul><li>doesn't work for 3 rd party scripts </li></ul>
    34. 38. technique 2: window onload <ul><li><iframe src=&quot;menu.php&quot; width=0 height=0 frameborder=0> </iframe> </li></ul><ul><li><script type=&quot;text/javascript&quot;> </li></ul><ul><li>var aExamples = [['couple-normal.php', 'Normal Script Src'], ...]; </li></ul><ul><li>function init() { </li></ul><ul><li>EFWS.Menu.createMenu('examplesbtn', aExamples); </li></ul><ul><li>} </li></ul><ul><li>if ( window.addEventListener ) { </li></ul><ul><li>window.addEventListener(&quot;load&quot;, init, false); </li></ul><ul><li>} </li></ul><ul><li>else if ( window.attachEvent ) { </li></ul><ul><li>window.attachEvent(&quot;onload&quot;, init); </li></ul><ul><li>} </li></ul><ul><li></script> </li></ul><ul><li>init() is called at window onload </li></ul><ul><li>must use async technique that blocks onload: </li></ul><ul><ul><li>Script in Iframe does this across most browsers </li></ul></ul><ul><li>init() called later than necessary </li></ul>
    35. 39. technique 3: timer <ul><li><script type=&quot;text/javascript&quot;> </li></ul><ul><li>var domscript = document.createElement('script'); </li></ul><ul><li>domscript.src = &quot;menu.js&quot;; </li></ul><ul><li>document.getElementsByTagName('head')[0].appendChild(domscript); </li></ul><ul><li>var aExamples = [['couple-normal.php', 'Normal Script Src'], ...]; </li></ul><ul><li>function init() { </li></ul><ul><li>EFWS.Menu.createMenu('examplesbtn', aExamples); </li></ul><ul><li>} </li></ul><ul><li>function initTimer(interval) { </li></ul><ul><li>if ( &quot;undefined&quot; === typeof(EFWS) ) { </li></ul><ul><li>setTimeout(initTimer, interval); </li></ul><ul><li>} </li></ul><ul><li>else { </li></ul><ul><li>init(); </li></ul><ul><li>} </li></ul><ul><li>} </li></ul><ul><li>initTimer(300); </li></ul><ul><li></script> </li></ul><ul><li>load if interval too low, delay if too high </li></ul><ul><li>slight increased maintenance – EFWS </li></ul>
    36. 40. John Resig's degrading script tags <ul><li><script src=&quot;menu-degrading.js&quot; type=&quot;text/javascript&quot;> </li></ul><ul><li>var aExamples = [['couple-normal.php', 'Normal Script Src'], ...]; </li></ul><ul><li>function init() { </li></ul><ul><li>EFWS.Menu.createMenu('examplesbtn', aExamples); </li></ul><ul><li>} </li></ul><ul><li>init(); </li></ul><ul><li></script> </li></ul>at the end of menu-degrading.js: var scripts = document.getElementsByTagName(&quot;script&quot;); var cntr = scripts.length; while ( cntr ) { var curScript = scripts[cntr-1]; if (curScript.src.indexOf(&quot;menu-degrading.js&quot;) != -1) { eval( curScript.innerHTML ); break; } cntr--; } http://ejohn.org/blog/degrading-script-tags/ cleaner clearer safer – inlined code not called if script fails no browser supports it
    37. 41. technique 4: degrading script tags <ul><li><script type=&quot;text/javascript&quot;> </li></ul><ul><li>var aExamples = [['couple-normal.php', 'Normal Script Src'],...]; </li></ul><ul><li>function init() { </li></ul><ul><li>EFWS.Menu.createMenu('examplesbtn', aExamples); </li></ul><ul><li>} </li></ul><ul><li>var domscript = document.createElement('script'); </li></ul><ul><li>domscript.src = &quot;menu-degrading.js&quot;; </li></ul><ul><li>if ( -1 != navigator.userAgent.indexOf(&quot;Opera&quot;) ) { </li></ul><ul><li>domscript.innerHTML = &quot;init();&quot;; </li></ul><ul><li>} </li></ul><ul><li>else { </li></ul><ul><li>domscript.text = &quot;init();&quot;; </li></ul><ul><li>} </li></ul><ul><li>document.getElementsByTagName('head')[0].appendChild(domscript); </li></ul><ul><li></script> </li></ul><ul><li>elegant, flexible (cool!) </li></ul><ul><li>not well known </li></ul><ul><li>doesn't work for 3 rd party scripts (unless...) </li></ul>
    38. 42. technique 5: script onload <ul><li><script type=&quot;text/javascript&quot;> </li></ul><ul><li>var aExamples = [['couple-normal.php', 'Normal Script Src'], ...]; </li></ul><ul><li>function init() { </li></ul><ul><li>EFWS.Menu.createMenu('examplesbtn', aExamples); </li></ul><ul><li>} </li></ul><ul><li>var domscript = document.createElement('script'); </li></ul><ul><li>domscript.src = &quot;menu.js&quot;; </li></ul><ul><li>domscript.onloadDone = false; </li></ul><ul><li>domscript.onload = function() { </li></ul><ul><li>if ( ! domscript.onloadDone ) { init(); } </li></ul><ul><li>domscript.onloadDone = true; </li></ul><ul><li>}; </li></ul><ul><li>domscript.onreadystatechange = function() { </li></ul><ul><li>if ( &quot;loaded&quot; === domscript.readyState ) { </li></ul><ul><li>if ( ! domscript.onloadDone ) { init(); } </li></ul><ul><li>domscript.onloadDone = true; </li></ul><ul><li>} </li></ul><ul><li>} </li></ul><ul><li>document.getElementsByTagName('head')[0].appendChild(domscript); </li></ul><ul><li></script> </li></ul><ul><li>pretty nice, medium complexity </li></ul>
    39. 43. <ul><li>what about </li></ul><ul><li>multiple scripts </li></ul><ul><li>that depend on each other, </li></ul><ul><li>and inlined code </li></ul><ul><li>that depends on the scripts? </li></ul><ul><li>two solutions: </li></ul><ul><ul><li>Managed XHR </li></ul></ul><ul><ul><li>DOM Element and Doc Write </li></ul></ul>
    40. 44. multiple script example: menutier.js <ul><li><script src=&quot;menu.js&quot; type=&quot;text/javascript&quot;></script> </li></ul><ul><li><script src=&quot;menutier.js&quot; type=&quot;text/javascript&quot;></script> </li></ul><ul><li><script type=&quot;text/javascript&quot;> </li></ul><ul><li>var aRaceConditions = [['couple-normal.php', 'Normal...]; </li></ul><ul><li>var aWorkarounds = [['hardcoded-callback.php', 'Hardcod...]; </li></ul><ul><li>var aMultipleScripts = [['managed-xhr.php', 'Managed XH...]; </li></ul><ul><li>var aLoadScripts = [['loadscript.php', 'loadScript'], ...]; </li></ul><ul><li>var aSubmenus = </li></ul><ul><li>[[&quot;Race Conditions&quot;, aRaceConditions], </li></ul><ul><li>[&quot;Workarounds&quot;, aWorkarounds], </li></ul><ul><li>[&quot;Multiple Scripts&quot;, aMultipleScripts], </li></ul><ul><li>[&quot;General Solution&quot;, aLoadScripts]]; </li></ul><ul><li>function init() { </li></ul><ul><li>EFWS.Menu.createTieredMenu('examplesbtn', aSubmenus); </li></ul><ul><li>} </li></ul><ul><li></script> </li></ul>
    41. 45. technique 1: managed XHR <ul><li><script type=&quot;text/javascript&quot;> </li></ul><ul><li>var aRaceConditions = [['couple-normal.php', 'Normal...]; </li></ul><ul><li>var aWorkarounds = [['hardcoded-callback.php', 'Hardcod...]; </li></ul><ul><li>var aMultipleScripts = [['managed-xhr.php', 'Managed XH...]; </li></ul><ul><li>var aLoadScripts = [['loadscript.php', 'loadScript'], ...]; </li></ul><ul><li>var aSubmenus = [[&quot;Race Conditions&quot;, aRaceConditions], ...]; </li></ul><ul><li>function init() { </li></ul><ul><li>EFWS.Menu.createTieredMenu('examplesbtn', aSubmenus); </li></ul><ul><li>} </li></ul><ul><li>EFWS.Script.loadScriptXhrInjection(&quot;menu.js&quot;, null, true); </li></ul><ul><li>EFWS.Script.loadScriptXhrInjection(&quot;menutier.js&quot;, init, true); </li></ul><ul><li></script> </li></ul><ul><li>XHR Injection asynchronous technique does not preserve order – we have to add that </li></ul>before after
    42. 46. EFWS.loadScriptXhrInjection <ul><li>// Load an external script. </li></ul><ul><li>// Optionally call a callback and preserve order. </li></ul><ul><li>loadScriptXhrInjection: function(url, onload, bOrder) { </li></ul><ul><li>var iQ = EFWS.Script.queuedScripts.length; </li></ul><ul><li>if ( bOrder ) { </li></ul><ul><li>var qScript = { response: null, onload: onload, done: false }; </li></ul><ul><li>EFWS.Script.queuedScripts[iQ] = qScript; </li></ul><ul><li>} </li></ul><ul><li>var xhrObj = EFWS.Script.getXHRObject(); </li></ul><ul><li>xhrObj.onreadystatechange = function() { </li></ul><ul><li>if ( xhrObj.readyState == 4 ) { </li></ul><ul><li>if ( bOrder ) { </li></ul><ul><li>EFWS.Script.queuedScripts[iQ].response = xhrObj.responseText; </li></ul><ul><li>EFWS.Script.injectScripts(); </li></ul><ul><li>} </li></ul><ul><li>else { </li></ul><ul><li>eval(xhrObj.responseText); </li></ul><ul><li>if ( onload ) { </li></ul><ul><li>onload(); </li></ul><ul><li>} </li></ul><ul><li>} </li></ul><ul><li>} </li></ul><ul><li>}; </li></ul><ul><li>xhrObj.open('GET', url, true); </li></ul><ul><li>xhrObj.send(''); </li></ul><ul><li>} </li></ul>process queue (next slide) or... eval now, call callback save response to queue add to queue (if bOrder)
    43. 47. EFWS.injectScripts <ul><li>// Process queued scripts to see if any are ready to inject. </li></ul><ul><li>injectScripts: function() { </li></ul><ul><li>var len = EFWS.Script.queuedScripts.length; </li></ul><ul><li>for ( var i = 0; i < len; i++ ) { </li></ul><ul><li>var qScript = EFWS.Script.queuedScripts[i]; </li></ul><ul><li>if ( ! qScript.done ) { </li></ul><ul><li>if ( ! qScript.response ) { </li></ul><ul><li>// STOP! need to wait for this response </li></ul><ul><li>break; </li></ul><ul><li>} </li></ul><ul><li>else { </li></ul><ul><li>eval(qScript.response); </li></ul><ul><li>if ( qScript.onload ) { </li></ul><ul><li>qScript.onload(); </li></ul><ul><li>} </li></ul><ul><li>qScript.done = true; </li></ul><ul><li>} </li></ul><ul><li>} </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>preserves external script order non-blocking works in all browsers couples with inlined code works with scripts across domains ready for this script, eval and call callback bail – need to wait to preserve order if not yet injected
    44. 48. technique 2: DOM Element and Doc Write Firefox & Opera – use Script DOM Element IE – use document.write Script Tag Safari, Chrome – no benefit; rely on Safari 4 and Chrome 2
    45. 49. EFWS.loadScripts <ul><li>loadScripts: function(aUrls, onload) { </li></ul><ul><li>// first pass: see if any of the scripts are on a different domain </li></ul><ul><li>var nUrls = aUrls.length; </li></ul><ul><li>var bDifferent = false; </li></ul><ul><li>for ( var i = 0; i < nUrls; i++ ) { </li></ul><ul><li>if ( EFWS.Script.differentDomain(aUrls[i]) ) { </li></ul><ul><li>bDifferent = true; </li></ul><ul><li>break; </li></ul><ul><li>} </li></ul><ul><li>} </li></ul><ul><li>// pick the best loading function </li></ul><ul><li>var loadFunc = EFWS.Script.loadScriptXhrInjection; </li></ul><ul><li>if ( bDifferent ) { </li></ul><ul><li>if ( -1 != navigator.userAgent.indexOf('Firefox') || </li></ul><ul><li>-1 != navigator.userAgent.indexOf('Opera') ) { </li></ul><ul><li>loadFunc = EFWS.Script.loadScriptDomElement; </li></ul><ul><li>} </li></ul><ul><li>else { </li></ul><ul><li>loadFunc = EFWS.Script.loadScriptDocWrite; </li></ul><ul><li>} </li></ul><ul><li>} </li></ul><ul><li>// second pass: load the scripts </li></ul><ul><li>for ( var i = 0; i < nUrls; i++ ) { </li></ul><ul><li>loadFunc(aUrls[i], ( i+1 == nUrls ? onload : null ), true); </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>
    46. 50. multiple scripts with dependencies <ul><li><script type=&quot;text/javascript&quot;> </li></ul><ul><li>var aRaceConditions = [['couple-normal.php', 'Normal...]; </li></ul><ul><li>var aWorkarounds = [['hardcoded-callback.php', 'Hardcod...]; </li></ul><ul><li>var aMultipleScripts = [['managed-xhr.php', 'Managed XH...]; </li></ul><ul><li>var aLoadScripts = [['loadscript.php', 'loadScript'], ...]; </li></ul><ul><li>var aSubmenus = [[&quot;Race Conditions&quot;, aRaceConditions], ...]; </li></ul><ul><li>function init() { </li></ul><ul><li>EFWS.Menu.createTieredMenu('examplesbtn', aSubmenus); </li></ul><ul><li>} </li></ul><ul><li>EFWS.Script.loadScripts([&quot;menu.js&quot;, &quot;menutier.js&quot;], init); </li></ul><ul><li></script> </li></ul><ul><li>scripts on same domain: </li></ul><ul><ul><li>order preserved, no blocking </li></ul></ul><ul><li>scripts on different domain: </li></ul><ul><ul><li>order preserved: all </li></ul></ul><ul><ul><li>loads scripts in parallel: all except Saf3, Chr1 </li></ul></ul><ul><ul><li>load script and image in parallel: FF, Saf4, Chr2 </li></ul></ul>
    47. 51. asynchronous scripts wrap-up
    48. 52. case study: Google Analytics <ul><li>recommended pattern: 1 </li></ul><ul><li><script type=&quot;text/javascript&quot;> </li></ul><ul><li>var gaJsHost = ((&quot;https:&quot; == document.location.protocol) ? &quot;https://ssl.&quot; : &quot;http://www.&quot;); </li></ul><ul><li>document.write(unescape(&quot;%3Cscript src='&quot; + gaJsHost + &quot;google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E&quot;)); </li></ul><ul><li></script> </li></ul><ul><li><script type=&quot;text/javascript&quot;> </li></ul><ul><li>var pageTracker = _gat._getTracker(&quot;UA-xxxxxx-x&quot;); </li></ul><ul><li>pageTracker._trackPageview(); </li></ul><ul><li></script> </li></ul><ul><li>document.write Script Tag approach blocks other resources </li></ul>1 http://www.google.com/support/analytics/bin/answer.py?hl=en&answer=55488
    49. 53. case study: dojox.analytics.Urchin 1 <ul><li>_loadGA: function(){ </li></ul><ul><li>var gaHost = (&quot;https:&quot; == document.location.protocol) ? </li></ul><ul><li>&quot;https://ssl.&quot; : &quot;http://www.&quot;; </li></ul><ul><li>dojo.create('script', { </li></ul><ul><li>src: gaHost + &quot;google-analytics.com/ga.js&quot; </li></ul><ul><li>}, dojo.doc.getElementsByTagName(&quot;head&quot;)[0]); </li></ul><ul><li>setTimeout(dojo.hitch(this, &quot;_checkGA&quot;), this.loadInterval); </li></ul><ul><li>}, </li></ul><ul><li>_checkGA: function(){ </li></ul><ul><li>setTimeout(dojo.hitch(this, !window[&quot;_gat&quot;] ? &quot;_checkGA&quot; : &quot;_gotGA&quot;), this.loadInterval); </li></ul><ul><li>}, </li></ul><ul><li>_gotGA: function(){ </li></ul><ul><li>this.tracker = _gat._getTracker(this.acct); ... </li></ul><ul><li>} </li></ul><ul><li>Script DOM Element approach </li></ul><ul><li>&quot;timer&quot; coupling technique (script onload better) </li></ul>1 http://docs.dojocampus.org/dojox/analytics/Urchin
    50. 54. asynchronous loading & coupling <ul><li>async technique: Script DOM Element </li></ul><ul><ul><li>easy, cross-browser </li></ul></ul><ul><ul><li>doesn't ensure script order </li></ul></ul><ul><li>coupling technique: script onload </li></ul><ul><ul><li>fairly easy, cross-browser </li></ul></ul><ul><ul><li>ensures execution order for external script and inlined code </li></ul></ul>
    51. 55. bad: stylesheet followed by inline script <ul><li>browsers download stylesheets in parallel with other resources that follow... </li></ul><ul><li>...unless the stylesheet is followed by an inline script </li></ul><ul><li>http://stevesouders.com/cuzillion/?ex=10021 </li></ul><ul><li>best to move inline scripts above stylesheets or below other resources </li></ul><ul><li>use Link, not @import </li></ul>
    52. 56. don't scatter inline scripts eBay MSN MySpace Wikipedia
    53. 57. iframes: most expensive DOM element <ul><li>load 100 empty elements of each type </li></ul><ul><li>tested in all major browsers 1 </li></ul>1 IE 6, 7, 8; FF 2, 3.0, 3.1b2; Safari 3.2, 4; Opera 9.63, 10; Chrome 1.0, 2.0
    54. 58. iframes block onload <ul><li>parent's onload doesn't fire until iframe and all its components are downloaded </li></ul><ul><li>workaround for Safari and Chrome: set iframe src in JavaScript </li></ul><ul><li><iframe id=iframe1 src=&quot;&quot;></iframe> </li></ul><ul><li><script type=&quot;text/javascript&quot;> </li></ul><ul><li>document.getElementById('iframe1').src=&quot; url &quot;; </li></ul><ul><li></script> </li></ul>
    55. 59. scripts block iframe <ul><li>no surprise – scripts in the parent block the iframe from loading </li></ul>IE Firefox Safari Chrome Opera script script script
    56. 60. stylesheets block iframe (IE, FF) <ul><li>surprise – stylesheets in the parent block the iframe or its resources in IE & Firefox </li></ul>IE Firefox Safari Chrome Opera stylesheet stylesheet stylesheet
    57. 61. stylesheets after iframe still block (FF) <ul><li>surprise – even moving the stylesheet after the iframe still causes the iframe's resources to be blocked in Firefox </li></ul>IE Firefox Safari Chrome Opera stylesheet stylesheet stylesheet
    58. 62. iframes: no free connections <ul><li>iframe shares connection pool with parent (here – 2 connections per server in IE 7) </li></ul>iframe parent
    59. 63. flush the document early <ul><li>gotchas: </li></ul><ul><ul><li>PHP output_buffering – ob_flush() </li></ul></ul><ul><ul><li>Transfer-Encoding: chunked </li></ul></ul><ul><ul><li>gzip – Apache's DeflateBufferSize before 2.2.8 </li></ul></ul><ul><ul><li>proxies and anti-virus software </li></ul></ul><ul><ul><li>browsers – Safari (1K), Chrome (2K) </li></ul></ul><ul><li>other languages: </li></ul><ul><ul><li>$| or FileHandle autoflush (Perl), flush (Python), ios.flush (Ruby) </li></ul></ul>call PHP's flush() html image image script html image image script
    60. 64. flushing and domain blocking <ul><li>you might need to move flushed resources to a domain different from the HTML doc </li></ul>case study: Google search blocked by HTML document different domains html image image script html image image script google image image script image 204
    61. 65. takeaways <ul><li>focus on the frontend </li></ul><ul><li>run YSlow: http://developer.yahoo.com/yslow </li></ul><ul><li>this year's focus: JavaScript </li></ul><ul><li>speed matters </li></ul>
    62. 66. impact on revenue <ul><li>Google: </li></ul><ul><li>Yahoo: </li></ul><ul><li>Amazon: </li></ul>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
    63. 67. cost savings <ul><li>hardware – reduced load </li></ul><ul><li>bandwidth – reduced response size </li></ul>http://billwscott.com/share/presentations/2008/stanford/HPWP-RealWorld.pdf
    64. 68. <ul><li>if you want </li></ul><ul><ul><li>better user experience </li></ul></ul><ul><ul><li>more revenue </li></ul></ul><ul><ul><li>reduced operating expenses </li></ul></ul><ul><li>the strategy is clear </li></ul><ul><li>Even Faster Web Sites </li></ul>
    65. 69. Steve Souders [email_address] http://stevesouders.com/docs/sxsw-20090314.ppt
    1. A particular slide catching your eye?

      Clipping is a handy way to collect important slides you want to go back to later.

    ×