资源描述
Steve Shttp:/ Faster Web SitesDisclaimer: This content does not necessarily reflect the opinions of my employer. 17% 83%iGoogle, primed cachethe importance of frontend performance9% 91%iGoogle, empty cache time spent on the frontendEmpty Cache Primed C 97% 97% 95% 81% 95% 81% 47% 0% 67% 0% 98% 94% 98% 98%en.wikipedia.org/wiki 94% 91% 97% 96% 98% 97% April 2008 14 RULES 1. MAKE FEWER HTTP REQUESTS2. USE A CDN3. ADD AN EXPIRES HEADER4. GZIP COMPONENTS5. PUT STYLESHEETS AT THE TOP6. PUT SCRIPTS AT THE BOTTOM7. AVOID CSS EXPRESSIONS8. MAKE JS AND CSS EXTERNAL9. REDUCE DNS LOOKUPS10.MINIFY JS11.AVOID REDIRECTS12.REMOVE DUPLICATE SCRIPTS13.CONFIGURE ETAGS14.MAKE AJAX CACHEABLE 25% discount code: ssouders25 Sept 2007 June 2009 Even Faster WebsitesSplit the initial payloadLoad scripts without blockingCouple asynchronous scriptsDont scatter inline scriptsSplit the dominant domainFlush the document earlyUse iframes sparinglySimplify 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) AOLeBayMySpaceWikipediaYah !why focus on JavaScript?YouTub scripts block blocks parallel downloads and renderinghttp:/ MSNScripts 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 asynchronous script loadingXHR EvalXHR InjectionScript in IframeScript DOM ElementScript Deferdocument.write Script Tag XHR Evalscript must have same domain as main pagemust refactor scriptvar xhrObj = getXHRObject();xhrObj.onreadystatechange = function() if ( xhrObj.readyState != 4 ) return; eval(xhrObj.responseText); ;xhrObj.open(GET, A.js, true);xhrObj.send(); http:/ XHR Injectionvar 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:/ Script in Iframe iframe must have same domain as main pagemust refactor script:/ access iframe from main pagewindow.frames0.createNewDiv();/ access main page from iframeparent.document.createElement(div); http:/ Script DOM Elementvar se = document.createElement(script);se.src = http:/ .appendChild(se); script and main page domains can differno need to refactor JavaScript http:/ Script Deferonly supported in IE (just landed in FF 3.1)script and main page domains can differno need to refactor JavaScript http:/ document.write Script Tagdocument.write( + );parallelization only works in IEparallel downloads for scripts, nothing elseall document.writes must be in same script block http:/ browser busy indicators browser busy indicatorsgood to show busy indicators when the user needs feedback bad when downloading in the background Ensure scripts execute in order:necessary when scripts have dependenciesIE: http:/ http:/ scripts executing in order:faster first script back is executed immediatelyhttp:/ ordered execution load scripts without blocking *Only other document.write scripts are downloaded in parallel (in the same script block). and the winner is.XHR EvalXHR InjectionScript in iframeScript DOM ElementScript DeferScript DOM ElementScript Defer Script DOM ElementScript DOM Element (FF)Script Defer (IE) XHR EvalXHR InjectionScript in iframeScript DOM Element (IE)XHR InjectionXHR EvalScript DOM Element (IE) Managed XHR InjectionManaged XHR EvalScript DOM ElementManaged XHR InjectionManaged XHR Eval Script DOM Element (FF)Script Defer (IE)Managed XHR EvalManaged XHR InjectionScript DOM Element (FF)Script Defer (IE)Managed XHR EvalManaged XHR Injectiondifferent domains same domainsno order preserve orderno orderno busy show busy show busyno busypreserve order load scripts without blockingdont let scripts block other downloadsyou can still control execution order, busy indicators, and onload eventWhat about inline scripts? synchronous JS example: menu.jsvar 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(); asynchronous JS example: menu.jsvar domscript = document.createElement(script);domscript.src = menu.js; 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 DOM element approach beforeafter load scripts without blocking *Only other document.write scripts are downloaded in parallel (in the same script block). !IE asynchronous scripts wrap-upwhat aboutinlined code that depends on the script? what aboutinlined 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 techniqueshardcoded callbackwindow onloadtimerdegrading script tagsscript onload technique 1: hardcoded callbackvar 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(domscript);init() is called from within menu.jsnot very flexibledoesnt work for 3rd party scripts 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 onloadmust use async technique that blocks onload:Script in Iframe does this across most browsersinit() called later than necessary technique 3: timervar 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 highslight increased maintenance EFWS John Resigs degrading script tagsvar aExamples = couple-normal.php, Normal Script Src, .;function init() EFWS.Menu.createMenu(examplesbtn, aExamples);init();at the end of menu-degrading.js:var scripts = document.getElementsByTagName(script);var cntr = scripts.length;while ( cntr ) var curScript = scriptscntr-1; if (curScript.src.indexOf(menu-degrading.js) != -1) eval( curScript.innerHTML ); break; cntr-;http:/ejohn.org/blog/degrading-script-tags/cleanerclearersafer inlined code not called if script failsno browser supports it technique 4: degrading script tagsvar 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 knowndoesnt work for 3rd party scripts (unless.) technique 5: script onloadvar 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() if ( ! domscript.onloadDone ) init(); domscript.onloadDone = true; ;domscript.onreadystatechange = function() if ( loaded = domscript.readyState ) if ( ! domscript.onloadDone ) init(); domscript.onloadDone = true; document.getElementsByTagName(head)0.appendChild(domscript);pretty nice, medium complexity what aboutmultiple 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.jsvar 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); technique 1: managed XHRvar 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 beforeafter 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.queuedScriptsiQ = qScript; var xhrObj = EFWS.Script.getXHRObject(); xhrObj.onreadystatechange = function() if ( xhrObj.readyState = 4 ) if ( bOrder ) EFWS.Script.queuedScriptsiQ.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 callbacksave response to queueadd 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.queuedScriptsi; 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 callbackbail need to wait to preserve orderif not yet injectedpreserves external script order non-blockingworks in all browserscouples with inlined codeworks with scripts across domains technique 2: DOM Element and Doc WriteFirefox rely on Safari 4 and Chrome 2 EFWS.loadScriptsloadScripts: 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(aUrlsi) ) 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(aUrlsi, ( i+1 = nUrls ? onload : null ), true); multiple scripts with dependenciesvar 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 blockingscripts on different domain:order preserved: allloads scripts in parallel: all except Saf3, Chr1load script and image in parallel: FF, Saf4, Chr2 asynchronous scripts wrap-up case study: Google Analyticsrecommended pattern:1var gaJsHost = (https: = document.location.protocol) ? https:/ssl. : http:/www.);document.write(unescape(%3Cscript src= + gaJsHost + google- type=text/javascript%3E%3C/script%3E);var pageTracker = _gat._getTracker(UA-xxxxxx-x);pageTracker._trackPageview();document.write Script Tag approach blocks other resources 1http:/ dojo.create(script, src: gaHost + google- , 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 approachtimer coupling technique (script onload better) 1http:/docs.dojocampus.org/dojox/analytics/Urchin asynchronous loading FF 2, 3.0, 3.1b2; Safari 3.2, 4; Opera 9.63, 10; Chrome 1.0, 2.0 iframes block onloadparents onload doesnt fire until iframe and all its components are downloadedworkaround for Safari and Chrome: set iframe src in JavaScriptdocument.getElementById(iframe1).src=url; scripts block iframeno surprise scripts in the parent block the iframe from loadingIEFirefoxSafari ChromeOpera scriptscriptscript stylesheets block iframe (IE, FF)surprise stylesheets in the parent block the iframe or its resources in IE & FirefoxIEFirefoxSafari ChromeOpera stylesheetstylesheetstylesheet stylesheets after iframe still block (FF)surprise even moving the stylesheet after the iframe still causes the iframes resources to be blocked in FirefoxIEFirefoxSafari ChromeOpera stylesheetstylesheetstylesheet iframes: no free connectionsiframe shares connection pool with parent (here 2 connections per server in IE 7)iframeparent flush the document earlygotchas: PHP output_buffering ob_flush() Transfer-Encoding: chunked gzip Apaches 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)htmlimageimagescripthtmlimageimagescript call PHPs flush() flushing and domain blockingyou might need to move flushed resources to a domain different from the HTML dochtmlimageimage scripthtmlimageimagescriptgoogle imageimagescriptimage204case study: Google search blocked by HTML documentdifferent domains takeawaysfocus on the frontendrun YSlow: http:/ years focus: JavaScriptspeed matters impact on revenueGoogle:Yahoo:Amazon: 1 http:/ http:/ +500 ms -20% traffic1+400 ms -5-9% full-page traffic2+100 ms -1% sales1 cost savingshardware reduced loadbandwidth reduced response size http:/ if you want better user experience more revenue reduced operating expensesthe strategy is clearEven Faster Web Sites Steve Shttp:/
展开阅读全文