SlideShare a Scribd company logo
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.
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 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%
14 RULES MAKE FEWER HTTP REQUESTS USE A CDN ADD AN EXPIRES HEADER GZIP COMPONENTS PUT STYLESHEETS AT THE TOP PUT SCRIPTS AT THE BOTTOM AVOID CSS EXPRESSIONS MAKE JS AND CSS EXTERNAL REDUCE DNS LOOKUPS MINIFY JS AVOID REDIRECTS REMOVE DUPLICATE SCRIPTS CONFIGURE ETAGS MAKE AJAX CACHEABLE
 
 
 
25% discount code: "ssouders25"
Sept 2007
June 2009
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)
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=10008
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
asynchronous script loading XHR Eval XHR Injection Script in Iframe Script DOM Element Script Defer document.write Script Tag
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
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
Script in Iframe <iframe  src='A.html'  width=0 height=0  frameborder=0 id=frame1></iframe>  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
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
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
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
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 Ensure scripts execute in order: necessary when scripts have dependencies IE:  http://stevesouders.com/cuzillion/?ex=10017 FF:  http://stevesouders.com/cuzillion/?ex=10018 Avoid scripts executing in order: faster – first script back is executed immediately http://stevesouders.com/cuzillion/?ex=10019
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 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
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?
 
synchronous JS example: menu.js <script src=&quot;menu.js&quot; type=&quot;text/javascript&quot;></script> <script type=&quot;text/javascript&quot;> 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(); </script>
asynchronous JS example: menu.js <script type=&quot;text/javascript&quot;> var domscript = document.createElement('script'); domscript.src = &quot;menu.js&quot;;  document.getElementsByTagName('head')[0].appendChild(domscript); 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(); </script> script DOM element approach
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 what about inlined code  that depends on the script?
what about inlined code  that depends on the script?
baseline coupling results (not good) *  Scripts download in parallel regardless of the Defer attribute. need a way to load scripts asynchronously AND preserve order
coupling techniques hardcoded callback window onload timer degrading script tags script onload
technique 1: hardcoded callback <script type=&quot;text/javascript&quot;> var aExamples = [['couple-normal.php', 'Normal Script Src'], ...]; function init() { EFWS.Menu.createMenu('examplesbtn', aExamples); } var domscript = document.createElement('script'); domscript.src = &quot;menu.js&quot;; document.getElementsByTagName('head')[0].appendChild(domscript); </script> init() is called from within menu.js not very flexible doesn't work for 3 rd  party scripts
technique 2: window onload <iframe src=&quot;menu.php&quot; width=0 height=0 frameborder=0> </iframe> <script type=&quot;text/javascript&quot;> var aExamples = [['couple-normal.php', 'Normal Script Src'], ...]; function init() { EFWS.Menu.createMenu('examplesbtn', aExamples); } if ( window.addEventListener ) { window.addEventListener(&quot;load&quot;, init, false); } else if ( window.attachEvent ) { window.attachEvent(&quot;onload&quot;, init); } </script> 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
technique 3: timer <script type=&quot;text/javascript&quot;> var domscript = document.createElement('script'); domscript.src = &quot;menu.js&quot;; 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 ( &quot;undefined&quot; === typeof(EFWS) ) { setTimeout(initTimer, interval); } else { init(); } } initTimer(300); </script> load if  interval  too low, delay if too high slight increased maintenance –  EFWS
John Resig's degrading script tags <script src=&quot;menu-degrading.js&quot; type=&quot;text/javascript&quot;> var aExamples = [['couple-normal.php', 'Normal Script Src'], ...]; function init() { EFWS.Menu.createMenu('examplesbtn', aExamples); } init(); </script> 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
technique 4: degrading script tags <script type=&quot;text/javascript&quot;> var aExamples = [['couple-normal.php', 'Normal Script Src'],...]; function init() { EFWS.Menu.createMenu('examplesbtn', aExamples); } var domscript = document.createElement('script'); domscript.src = &quot;menu-degrading.js&quot;; if ( -1 != navigator.userAgent.indexOf(&quot;Opera&quot;) ) { domscript.innerHTML = &quot;init();&quot;; } else { domscript.text = &quot;init();&quot;; } document.getElementsByTagName('head')[0].appendChild(domscript); </script> elegant, flexible (cool!) not well known doesn't work for 3 rd  party scripts (unless...)
technique 5: script onload <script type=&quot;text/javascript&quot;> var aExamples = [['couple-normal.php', 'Normal Script Src'], ...]; function init() { EFWS.Menu.createMenu('examplesbtn', aExamples); } var domscript = document.createElement('script'); domscript.src = &quot;menu.js&quot;; domscript.onloadDone = false; domscript.onload = function() {  if ( ! domscript.onloadDone ) { init(); } domscript.onloadDone = true;  }; domscript.onreadystatechange = function() {  if ( &quot;loaded&quot; === domscript.readyState ) {  if ( ! domscript.onloadDone ) { init(); } domscript.onloadDone = true;  } } document.getElementsByTagName('head')[0].appendChild(domscript); </script> pretty nice, medium complexity
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
multiple script example: menutier.js <script src=&quot;menu.js&quot; type=&quot;text/javascript&quot;></script> <script src=&quot;menutier.js&quot; type=&quot;text/javascript&quot;></script> <script type=&quot;text/javascript&quot;> 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 =  [[&quot;Race Conditions&quot;, aRaceConditions],  [&quot;Workarounds&quot;, aWorkarounds], [&quot;Multiple Scripts&quot;, aMultipleScripts], [&quot;General Solution&quot;, aLoadScripts]]; function init() { EFWS.Menu.createTieredMenu('examplesbtn', aSubmenus); } </script>
technique 1: managed XHR <script type=&quot;text/javascript&quot;> 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 = [[&quot;Race Conditions&quot;, aRaceConditions], ...]; function init() { EFWS.Menu.createTieredMenu('examplesbtn', aSubmenus); } EFWS.Script.loadScriptXhrInjection(&quot;menu.js&quot;, null, true); EFWS.Script.loadScriptXhrInjection(&quot;menutier.js&quot;, init,   true); </script> XHR Injection asynchronous technique does not preserve order – we have to add that  before after
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)
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; } } } } 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
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
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); } }
multiple scripts with dependencies <script type=&quot;text/javascript&quot;> 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 = [[&quot;Race Conditions&quot;, aRaceConditions], ...]; function init() { EFWS.Menu.createTieredMenu('examplesbtn', aSubmenus); } EFWS.Script.loadScripts([&quot;menu.js&quot;, &quot;menutier.js&quot;], init); </script> 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
asynchronous scripts wrap-up
case study: Google Analytics recommended pattern: 1 <script type=&quot;text/javascript&quot;> var gaJsHost = ((&quot;https:&quot; == document.location.protocol) ? &quot;https://ssl.&quot; : &quot;http://www.&quot;); document.write(unescape(&quot;%3Cscript src='&quot; + gaJsHost + &quot;google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E&quot;)); </script> <script type=&quot;text/javascript&quot;> var pageTracker = _gat._getTracker(&quot;UA-xxxxxx-x&quot;); pageTracker._trackPageview(); </script> document.write  Script Tag approach blocks other resources 1 http://www.google.com/support/analytics/bin/answer.py?hl=en&answer=55488
case study: dojox.analytics.Urchin 1 _loadGA: function(){ var gaHost = (&quot;https:&quot; == document.location.protocol) ?  &quot;https://ssl.&quot; : &quot;http://www.&quot;; dojo.create('script', { src: gaHost + &quot;google-analytics.com/ga.js&quot; }, dojo.doc.getElementsByTagName(&quot;head&quot;)[0]); setTimeout(dojo.hitch(this, &quot;_checkGA&quot;), this.loadInterval); }, _checkGA: function(){ setTimeout(dojo.hitch(this, !window[&quot;_gat&quot;] ? &quot;_checkGA&quot; : &quot;_gotGA&quot;), this.loadInterval); }, _gotGA: function(){ this.tracker = _gat._getTracker(this.acct); ... } Script DOM Element approach &quot;timer&quot; coupling technique  (script onload better) 1 http://docs.dojocampus.org/dojox/analytics/Urchin
asynchronous loading & coupling async technique: Script DOM Element easy, cross-browser doesn't ensure script order coupling technique: script onload fairly easy, cross-browser ensures execution order for external script and inlined code
bad: stylesheet followed by inline script browsers download stylesheets in parallel with other resources that follow... ...unless the stylesheet is followed by an inline script http://stevesouders.com/cuzillion/?ex=10021 best to move inline scripts above stylesheets or below other resources use Link, not @import
don't scatter inline scripts eBay MSN MySpace Wikipedia
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 parent's onload doesn't fire until iframe and all its components are downloaded workaround for Safari and Chrome: set iframe src in JavaScript <iframe id=iframe1 src=&quot;&quot;></iframe> <script type=&quot;text/javascript&quot;> document.getElementById('iframe1').src=&quot; url &quot;; </script>
scripts block iframe no surprise – scripts in the parent block the iframe from loading IE Firefox Safari Chrome Opera script script script
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 stylesheet stylesheet
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 stylesheet stylesheet
iframes: no free connections iframe shares connection pool with parent (here – 2 connections per server in IE 7) iframe parent
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) other languages:  $|  or  FileHandle autoflush  (Perl),  flush  (Python),  ios.flush  (Ruby) call PHP's  flush() html image image script html image image script
flushing and domain blocking you might need to move flushed resources to a domain different from the HTML doc case study: Google search blocked by HTML document different domains html image image script html image image script google image image script image 204
takeaways focus on the frontend run YSlow:  http://developer.yahoo.com/yslow this year's focus: JavaScript speed matters
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
cost savings hardware – reduced load bandwidth – reduced response size 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 Even Faster Web Sites
Steve Souders [email_address] http://stevesouders.com/docs/sxsw-20090314.ppt

More Related Content

PPT
Even Faster Web Sites at jQuery Conference '09
PPT
Web20expo 20080425
PPTX
Fast by Default
PDF
Workshop: Functional testing made easy with PHPUnit & Selenium (phpCE Poland,...
PPT
PDF
Selenium & PHPUnit made easy with Steward (Berlin, April 2017)
PPT
Widget Summit 2008
PPTX
Your Script Just Killed My Site
Even Faster Web Sites at jQuery Conference '09
Web20expo 20080425
Fast by Default
Workshop: Functional testing made easy with PHPUnit & Selenium (phpCE Poland,...
Selenium & PHPUnit made easy with Steward (Berlin, April 2017)
Widget Summit 2008
Your Script Just Killed My Site

What's hot (17)

PPTX
An introduction to PhantomJS: A headless browser for automation test.
ZIP
Palestra VCR
PPT
Owasp Wasc App Sec2007 San Jose Finding Vulnsin Flash Apps
PDF
Thomas Fuchs Presentation
PDF
Detecting headless browsers
PDF
Building Realtime Apps with Ember.js and WebSockets
PPTX
A few good JavaScript development tools
PPTX
Client-side JavaScript Vulnerabilities
KEY
Like a Genie from a Lamp: Headless JavaScript Unit Testing with Jasmine and P...
PPT
What's new in Rails 2?
PDF
"今" 使えるJavaScriptのトレンド
ODP
PPT
Flash Security, OWASP Chennai
KEY
Rails Presentation (Anton Dmitriyev)
PDF
Uazaa uma-farsa-parte 2
PPTX
Cache is King
An introduction to PhantomJS: A headless browser for automation test.
Palestra VCR
Owasp Wasc App Sec2007 San Jose Finding Vulnsin Flash Apps
Thomas Fuchs Presentation
Detecting headless browsers
Building Realtime Apps with Ember.js and WebSockets
A few good JavaScript development tools
Client-side JavaScript Vulnerabilities
Like a Genie from a Lamp: Headless JavaScript Unit Testing with Jasmine and P...
What's new in Rails 2?
"今" 使えるJavaScriptのトレンド
Flash Security, OWASP Chennai
Rails Presentation (Anton Dmitriyev)
Uazaa uma-farsa-parte 2
Cache is King
Ad

Viewers also liked (11)

PPT
Web前端开发的现状和未来
PPT
前端开发的那些事儿
PDF
第一节课:前端的美好时代
PDF
第一节课:web前端技术程序员编程能力成长之路
PDF
Html&css培训 舒克
PPTX
给聚划算后端开发的前端培训
PPTX
浅析浏览器解析和渲染
PDF
Clasificación Ruta Corta 53km
PDF
Рекомендації місії Пета Кокса
PDF
915 Foundation 2009 Diagnostics2
PDF
Using Indirect Covariance Spectra to Identify Artifact Responses in Unsymmetr...
Web前端开发的现状和未来
前端开发的那些事儿
第一节课:前端的美好时代
第一节课:web前端技术程序员编程能力成长之路
Html&css培训 舒克
给聚划算后端开发的前端培训
浅析浏览器解析和渲染
Clasificación Ruta Corta 53km
Рекомендації місії Пета Кокса
915 Foundation 2009 Diagnostics2
Using Indirect Covariance Spectra to Identify Artifact Responses in Unsymmetr...
Ad

Similar to Google在Web前端方面的经验 (20)

PPT
Even Faster Web Sites at The Ajax Experience
PPT
Web 2.0 Expo: Even Faster Web Sites
PPT
Oscon 20080724
PPT
Teflon - Anti Stick for the browser attack surface
PPT
Sanjeev ghai 12
DOCX
Javascript
PDF
HTML5: huh, what is it good for?
PPT
Rey Bango - HTML5: polyfills and shims
ODP
Integration Testing in Python
PPT
Aspnet2 Overview
PPTX
Compatibility Detector Tool of Chrome extensions
PPT
Ajax to the Moon
PPT
WordPress and Ajax
PPT
AppengineJS
ODP
10 Things You're Not Doing [IBM Lotus Notes Domino Application Development]
ODP
Implementing Comet using PHP
PDF
High Performance JavaScript 2011
PPT
Lecture 5 - Comm Lab: Web @ ITP
PPT
2 Asp Dot Net Ajax Extensions
PPTX
High Performance JavaScript (CapitolJS 2011)
Even Faster Web Sites at The Ajax Experience
Web 2.0 Expo: Even Faster Web Sites
Oscon 20080724
Teflon - Anti Stick for the browser attack surface
Sanjeev ghai 12
Javascript
HTML5: huh, what is it good for?
Rey Bango - HTML5: polyfills and shims
Integration Testing in Python
Aspnet2 Overview
Compatibility Detector Tool of Chrome extensions
Ajax to the Moon
WordPress and Ajax
AppengineJS
10 Things You're Not Doing [IBM Lotus Notes Domino Application Development]
Implementing Comet using PHP
High Performance JavaScript 2011
Lecture 5 - Comm Lab: Web @ ITP
2 Asp Dot Net Ajax Extensions
High Performance JavaScript (CapitolJS 2011)

More from yiditushe (20)

DOC
Spring入门纲要
PDF
J Bpm4 1中文用户手册
PPT
性能测试实践2
PPT
性能测试实践1
PPT
性能测试技术
PPT
Load runner测试技术
PPT
J2 ee性能测试
PPT
面向对象的Js培训
PDF
Flex3中文教程
PDF
开放源代码的全文检索Lucene
PDF
基于分词索引的全文检索技术介绍
PDF
Lucene In Action
DOC
Lucene2 4学习笔记1
DOC
Lucene2 4 Demo
PDF
Lucene 全文检索实践
PDF
Lucene 3[1] 0 原理与代码分析
PPT
7 面向对象设计原则
PPT
10 团队开发
PPT
9 对象持久化与数据建模
PPT
8 Uml构架建模
Spring入门纲要
J Bpm4 1中文用户手册
性能测试实践2
性能测试实践1
性能测试技术
Load runner测试技术
J2 ee性能测试
面向对象的Js培训
Flex3中文教程
开放源代码的全文检索Lucene
基于分词索引的全文检索技术介绍
Lucene In Action
Lucene2 4学习笔记1
Lucene2 4 Demo
Lucene 全文检索实践
Lucene 3[1] 0 原理与代码分析
7 面向对象设计原则
10 团队开发
9 对象持久化与数据建模
8 Uml构架建模

Google在Web前端方面的经验

  • 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. the importance of frontend performance 17% 83% iGoogle, primed cache 9% 91% iGoogle, empty cache
  • 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. 14 RULES MAKE FEWER HTTP REQUESTS USE A CDN ADD AN EXPIRES HEADER GZIP COMPONENTS PUT STYLESHEETS AT THE TOP PUT SCRIPTS AT THE BOTTOM AVOID CSS EXPRESSIONS MAKE JS AND CSS EXTERNAL REDUCE DNS LOOKUPS MINIFY JS AVOID REDIRECTS REMOVE DUPLICATE SCRIPTS CONFIGURE ETAGS MAKE AJAX CACHEABLE
  • 5.  
  • 6.  
  • 7.  
  • 8. 25% discount code: &quot;ssouders25&quot;
  • 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)
  • 12. why focus on JavaScript? AOL eBay Facebook MySpace Wikipedia Yahoo! YouTube
  • 13. scripts block <script src=&quot;A.js&quot;> blocks parallel downloads and rendering http://stevesouders.com/cuzillion/?ex=10008
  • 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
  • 15. asynchronous script loading XHR Eval XHR Injection Script in Iframe Script DOM Element Script Defer document.write Script Tag
  • 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
  • 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
  • 18. Script in Iframe <iframe src='A.html' width=0 height=0 frameborder=0 id=frame1></iframe> 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
  • 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
  • 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
  • 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
  • 23. browser busy indicators good to show busy indicators when the user needs feedback bad when downloading in the background
  • 24. ensure/avoid ordered execution Ensure scripts execute in order: necessary when scripts have dependencies IE: http://stevesouders.com/cuzillion/?ex=10017 FF: http://stevesouders.com/cuzillion/?ex=10018 Avoid scripts executing in order: faster – first script back is executed immediately http://stevesouders.com/cuzillion/?ex=10019
  • 25. load scripts without blocking * Only other document.write scripts are downloaded in parallel (in the same script block).
  • 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
  • 27. 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?
  • 28.  
  • 29. synchronous JS example: menu.js <script src=&quot;menu.js&quot; type=&quot;text/javascript&quot;></script> <script type=&quot;text/javascript&quot;> 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(); </script>
  • 30. asynchronous JS example: menu.js <script type=&quot;text/javascript&quot;> var domscript = document.createElement('script'); domscript.src = &quot;menu.js&quot;; document.getElementsByTagName('head')[0].appendChild(domscript); 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(); </script> script DOM element approach
  • 32. load scripts without blocking * Only other document.write scripts are downloaded in parallel (in the same script block). !IE
  • 33. asynchronous scripts wrap-up what about inlined code that depends on the script?
  • 34. what about inlined code that depends on the script?
  • 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
  • 36. coupling techniques hardcoded callback window onload timer degrading script tags script onload
  • 37. technique 1: hardcoded callback <script type=&quot;text/javascript&quot;> var aExamples = [['couple-normal.php', 'Normal Script Src'], ...]; function init() { EFWS.Menu.createMenu('examplesbtn', aExamples); } var domscript = document.createElement('script'); domscript.src = &quot;menu.js&quot;; document.getElementsByTagName('head')[0].appendChild(domscript); </script> init() is called from within menu.js not very flexible doesn't work for 3 rd party scripts
  • 38. technique 2: window onload <iframe src=&quot;menu.php&quot; width=0 height=0 frameborder=0> </iframe> <script type=&quot;text/javascript&quot;> var aExamples = [['couple-normal.php', 'Normal Script Src'], ...]; function init() { EFWS.Menu.createMenu('examplesbtn', aExamples); } if ( window.addEventListener ) { window.addEventListener(&quot;load&quot;, init, false); } else if ( window.attachEvent ) { window.attachEvent(&quot;onload&quot;, init); } </script> 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
  • 39. technique 3: timer <script type=&quot;text/javascript&quot;> var domscript = document.createElement('script'); domscript.src = &quot;menu.js&quot;; 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 ( &quot;undefined&quot; === typeof(EFWS) ) { setTimeout(initTimer, interval); } else { init(); } } initTimer(300); </script> load if interval too low, delay if too high slight increased maintenance – EFWS
  • 40. John Resig's degrading script tags <script src=&quot;menu-degrading.js&quot; type=&quot;text/javascript&quot;> var aExamples = [['couple-normal.php', 'Normal Script Src'], ...]; function init() { EFWS.Menu.createMenu('examplesbtn', aExamples); } init(); </script> 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
  • 41. technique 4: degrading script tags <script type=&quot;text/javascript&quot;> var aExamples = [['couple-normal.php', 'Normal Script Src'],...]; function init() { EFWS.Menu.createMenu('examplesbtn', aExamples); } var domscript = document.createElement('script'); domscript.src = &quot;menu-degrading.js&quot;; if ( -1 != navigator.userAgent.indexOf(&quot;Opera&quot;) ) { domscript.innerHTML = &quot;init();&quot;; } else { domscript.text = &quot;init();&quot;; } document.getElementsByTagName('head')[0].appendChild(domscript); </script> elegant, flexible (cool!) not well known doesn't work for 3 rd party scripts (unless...)
  • 42. technique 5: script onload <script type=&quot;text/javascript&quot;> var aExamples = [['couple-normal.php', 'Normal Script Src'], ...]; function init() { EFWS.Menu.createMenu('examplesbtn', aExamples); } var domscript = document.createElement('script'); domscript.src = &quot;menu.js&quot;; domscript.onloadDone = false; domscript.onload = function() { if ( ! domscript.onloadDone ) { init(); } domscript.onloadDone = true; }; domscript.onreadystatechange = function() { if ( &quot;loaded&quot; === domscript.readyState ) { if ( ! domscript.onloadDone ) { init(); } domscript.onloadDone = true; } } document.getElementsByTagName('head')[0].appendChild(domscript); </script> pretty nice, medium complexity
  • 43. 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
  • 44. multiple script example: menutier.js <script src=&quot;menu.js&quot; type=&quot;text/javascript&quot;></script> <script src=&quot;menutier.js&quot; type=&quot;text/javascript&quot;></script> <script type=&quot;text/javascript&quot;> 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 = [[&quot;Race Conditions&quot;, aRaceConditions], [&quot;Workarounds&quot;, aWorkarounds], [&quot;Multiple Scripts&quot;, aMultipleScripts], [&quot;General Solution&quot;, aLoadScripts]]; function init() { EFWS.Menu.createTieredMenu('examplesbtn', aSubmenus); } </script>
  • 45. technique 1: managed XHR <script type=&quot;text/javascript&quot;> 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 = [[&quot;Race Conditions&quot;, aRaceConditions], ...]; function init() { EFWS.Menu.createTieredMenu('examplesbtn', aSubmenus); } EFWS.Script.loadScriptXhrInjection(&quot;menu.js&quot;, null, true); EFWS.Script.loadScriptXhrInjection(&quot;menutier.js&quot;, init, true); </script> XHR Injection asynchronous technique does not preserve order – we have to add that before after
  • 46. 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)
  • 47. 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; } } } } 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
  • 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
  • 49. 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); } }
  • 50. multiple scripts with dependencies <script type=&quot;text/javascript&quot;> 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 = [[&quot;Race Conditions&quot;, aRaceConditions], ...]; function init() { EFWS.Menu.createTieredMenu('examplesbtn', aSubmenus); } EFWS.Script.loadScripts([&quot;menu.js&quot;, &quot;menutier.js&quot;], init); </script> 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
  • 52. case study: Google Analytics recommended pattern: 1 <script type=&quot;text/javascript&quot;> var gaJsHost = ((&quot;https:&quot; == document.location.protocol) ? &quot;https://ssl.&quot; : &quot;http://www.&quot;); document.write(unescape(&quot;%3Cscript src='&quot; + gaJsHost + &quot;google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E&quot;)); </script> <script type=&quot;text/javascript&quot;> var pageTracker = _gat._getTracker(&quot;UA-xxxxxx-x&quot;); pageTracker._trackPageview(); </script> document.write Script Tag approach blocks other resources 1 http://www.google.com/support/analytics/bin/answer.py?hl=en&answer=55488
  • 53. case study: dojox.analytics.Urchin 1 _loadGA: function(){ var gaHost = (&quot;https:&quot; == document.location.protocol) ? &quot;https://ssl.&quot; : &quot;http://www.&quot;; dojo.create('script', { src: gaHost + &quot;google-analytics.com/ga.js&quot; }, dojo.doc.getElementsByTagName(&quot;head&quot;)[0]); setTimeout(dojo.hitch(this, &quot;_checkGA&quot;), this.loadInterval); }, _checkGA: function(){ setTimeout(dojo.hitch(this, !window[&quot;_gat&quot;] ? &quot;_checkGA&quot; : &quot;_gotGA&quot;), this.loadInterval); }, _gotGA: function(){ this.tracker = _gat._getTracker(this.acct); ... } Script DOM Element approach &quot;timer&quot; coupling technique (script onload better) 1 http://docs.dojocampus.org/dojox/analytics/Urchin
  • 54. asynchronous loading & coupling async technique: Script DOM Element easy, cross-browser doesn't ensure script order coupling technique: script onload fairly easy, cross-browser ensures execution order for external script and inlined code
  • 55. bad: stylesheet followed by inline script browsers download stylesheets in parallel with other resources that follow... ...unless the stylesheet is followed by an inline script http://stevesouders.com/cuzillion/?ex=10021 best to move inline scripts above stylesheets or below other resources use Link, not @import
  • 56. don't scatter inline scripts eBay MSN MySpace Wikipedia
  • 57. 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
  • 58. 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 <iframe id=iframe1 src=&quot;&quot;></iframe> <script type=&quot;text/javascript&quot;> document.getElementById('iframe1').src=&quot; url &quot;; </script>
  • 59. scripts block iframe no surprise – scripts in the parent block the iframe from loading IE Firefox Safari Chrome Opera script script script
  • 60. 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 stylesheet stylesheet
  • 61. 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 stylesheet stylesheet
  • 62. iframes: no free connections iframe shares connection pool with parent (here – 2 connections per server in IE 7) iframe parent
  • 63. 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) other languages: $| or FileHandle autoflush (Perl), flush (Python), ios.flush (Ruby) call PHP's flush() html image image script html image image script
  • 64. flushing and domain blocking you might need to move flushed resources to a domain different from the HTML doc case study: Google search blocked by HTML document different domains html image image script html image image script google image image script image 204
  • 65. takeaways focus on the frontend run YSlow: http://developer.yahoo.com/yslow this year's focus: JavaScript speed matters
  • 66. 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
  • 67. cost savings hardware – reduced load bandwidth – reduced response size http://billwscott.com/share/presentations/2008/stanford/HPWP-RealWorld.pdf
  • 68. if you want better user experience more revenue reduced operating expenses the strategy is clear Even Faster Web Sites
  • 69. Steve Souders [email_address] http://stevesouders.com/docs/sxsw-20090314.ppt