Optimising Web Application Frontend


Published on

Published in: Technology, Design
  • Be the first to comment

No Downloads
Total views
On SlideShare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide

Optimising Web Application Frontend

  1. 1. Optimising Webapplication frontend Tomáš Kramár, @tkramar
  2. 2. What happens when I type address into browser?
  3. 3. GET /index.html GET /index.htmlBrowser Server
  4. 4. GET /index.html index.html GET /assets/favicon.ico favicon.icoBrowser Server
  5. 5. GET /index.html index.html GET /assets/favicon.ico favicon.icoBrowser Server GET /assets/application.css application.css
  6. 6. GET /index.html index.html GET /assets/favicon.ico favicon.icoBrowser Server GET /assets/application.css application.css GET /assets/bg.png bg.png
  7. 7. GET /index.html index.html Backend GET /assets/favicon.ico favicon.icoBrowser Server GET /assets/application.css application.css GET /assets/bg.png bg.png
  8. 8. Page Load Time = Backend Time + Frontend Time
  9. 9. Optimisation rule #1Optimise only when it makes sense
  10. 10. Optimisation rule #1Optimise only when it makes sense * http://www.stevesouders.com/blog/2012/02/10/the-performance-golden-rule/
  11. 11. Waterfall / Firebug
  12. 12. Demo
  13. 13. Sprechen Sie Firebug?● Blocking - request is queued and waiting● DNS Lookup - time to resolve hostname● Connection - time to create TCP connection● Sending - sending request headers● Waiting - backend is busy now● Receiving - reading the response● blue and red vertical lines: DOMContentLoaded and load events ○ http://ie.microsoft. com/testdrive/HTML5/DOMContentLoaded/Default. html
  14. 14. Optimisation rule #2Download resources in parallel
  15. 15. Resource downloading rulesClients that use persistent connectionsSHOULD limit the number of simultaneousconnections that they maintain to a givenserver. A single-user client SHOULD NOTmaintain more than 2 connections with anyserver or proxy. -- RFC 2616 (HTTP 1.1)
  16. 16. Demo
  17. 17. Solution: Asset subdomains● assets01.domain.com● assets02.domain.com● ...● Extra: ○ Cookie-less domain ○ HTTP Keep-Alive● Pitfalls: ○ DNS lookups
  18. 18. Optimisation rule #3Fastest request is the one that doesnt happen
  19. 19. Avada Kedavra Request!● Merge● Inline● Sprite● Cache
  20. 20. Merge● Merge multiple CSS files into a single file● Merge multiple JS files into a single file ○ -> Rails/Sprockets/Asset pipeline $ cat app/assets/javascripts/application.js //= require jquery_ujs //= require_tree ./vendor //= require document_viewer/dv //= require_tree .
  21. 21. Inline● Inline JavaScript ○ Replace <script src=".."></script> with <script>//code</script>● Inline CSS ○ <style>body { color: red; }</style>● Usable only for small resources, larger resources benefit more from caching● Inline images using data URIs ○ <img src=data:image/jpeg;base64, /9j/4AAQSkZJR/> ○ background-image: url(data:image/jpeg;base64, /9j/4AAQS); ○ Pitfalls: size limit, IE <= 7
  22. 22. Demo
  23. 23. CSS spritesMerge multiple imagesinto one, use background-position to place.
  24. 24. CachingFirst request: cache miss, hit the server, obtaintokenAdditional requests: use the token from firstrequest to make conditional requestConditional requests● If-None-Match● If-Modified-Since
  25. 25. If-None-Match: First requestFirst request:Request headers:GET /Response headers:Status: 200 OKEtag: "be5c5a3edac0592617693fa..."
  26. 26. If-None-Match: Next requestsRequest headers:GET /If-None-Match: "be5c5a3edac0592617693fa..."Response headers:Status: 304 Not ModifiedEtag: "be5c5a3edac0592617693fa..."
  27. 27. If-None-Match● Server needs to calculate ETag (fingerprint)● How? ○ it depends ○ easiest way: generate output, calculate hash● If ETag matches, send 304 Not Modified, no response body● You save on the data transfer ("Receiving" in Firebug)
  28. 28. If-Modified-Since: First requestRequest headers:GET /Response headers:Status: 200 OKExpires: Thu, 31 Dec 2037 23:55:55 GMTLast-Modified: Mon, 30 Jan 2012 13:36:26 GMT
  29. 29. If-Modified-Since: Local cacheRequest headers:noneResponse headers:noneCurrent time is < resources Expire header
  30. 30. If-Modified-Since: Forced refreshRequest headers:GET /If-Modified-Since: Mon, 30 Jan 2012 13:36:26GMTResponse headers:Status: 304 Not ModifiedExpires: Thu, 31 Dec 2037 23:55:55 GMTLast-Modified: Mon, 30 Jan 2012 13:36:26 GMT
  31. 31. RefreshRegular click on a link: uses local cache and norequest is madeF5: Skips local cache, sends If-Modified-Sincerequest, potentially 304-ingCtrl+F5: Sends requests without If-None-Match and If-Modified-Since
  32. 32. Demo
  33. 33. Far future expires strategySet the Expires header far in the futureFAQQ: But what if I need to change the resource?A: Use a different name application-f7fd224c9bc0fd4c2f7.css fingerprint
  34. 34. Rails gotchaAsset pipeline sets fingerprints, but not Expireheaders. You need to DIY.Nginx:location ~* .(js|css|jpg|jpeg|gif|png|swf|ico)$ { expires max;}
  35. 35. Optimisation rule #3If you need to make the request, make theresponse (and request) small.
  36. 36. Follow these rules● Minify and gzip CSS and JavaScript.● Do not send/set cookies unless necessary (asset subdomains)● Do not scale images in HTML ○ do not use <img size=""/>, scale image on server● Optimize images ○ http://www.smushit.com/ysmush.it/
  37. 37. Optimisation rule #4If possible, defer parsing of JavaScripts
  38. 38. How browsers process JavaScriptWhen browsers encounter <script> tag, thescript is downloaded, parsed and executed.Main parsing thread is busy processing thescript.Speculative parsing continues downloading andbuilding the DOM in the background, so mainthread can pull this fast, but you still pay theparse and execute penalty.
  39. 39. Do you need to execute the scriptimmediately?Probably not.Is your code waiting for document.ready?Mouse action?Then definitely not.
  40. 40. Then defer it<script type="text/javascript">function downloadJSAtOnload() { var element = document.createElement("script"); element.src = "application.js"; document.body.appendChild(element);}if (window.addEventListener) window.addEventListener("load", downloadJSAtOnload,false);else if (window.attachEvent) window.attachEvent("onload", downloadJSAtOnload);else window.onload = downloadJSAtOnload;</script>
  41. 41. Demo
  42. 42. Optimisation rule #5As a last resort: simply cheat.
  43. 43. Perceived speed mattersMove all CSS to the top so DOM nodes can berendered progressivelyIf you cannot defer, at least move <script> tothe bottom of the page.Post-load parts of page that are slow to render.
  44. 44. Optimisation rule #5Think globally.
  45. 45. Location mattersDownloading image placed on a server inSlovakia is relatively cheap than saydownloading the same image from Canada.
  46. 46. Use CDN (content delivery network)Servers spread through the world.Use CDN on common assets (jQuery etc.).Chance is, you will get a cache hit.https://developers.google.com/speed/libraries/
  47. 47. ToolsWeb Page Test, http://www.webpagetest.org/Newrelic RUM, http://newrelic.com
  48. 48. Is this all I can do?Definitely not.Google Page Speed, http://developers.google.com/speed/pagespeed/YSlow, http://yslow.org/
  49. 49. FutureSPDYHTTP archiveslocal storage
  50. 50. Summary1. Optimise only when it makes sense2. Download resources in parallel3. Fastest request is the one that doesnt happen4. If you need to make the request, make the response (and request) small5. If possible, defer parsing of JavaScripts6. Think globally