Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Polymer Performance Patterns

Eric Bidelman
September 15, 2015

Polymer Performance Patterns

Polymer Summit 2016 presentation. Tips, tricks, patterns for building faster Polymer and web component-based apps.

Video: https://www.youtube.com/watch?v=Yr84DpNaMfk

Eric Bidelman

September 15, 2015
Tweet

More Decks by Eric Bidelman

Other Decks in Technology

Transcript

  1. @ebidel @polymer #polymersummit optimizing load & first paint LOAD optimization

    tips for minimizing in-app jank RENDER future APIs for performance FUTURE
  2. the extent to which an investment is profitable, especially in

    relation to other investments. PERFORMANCE noun -
  3. the extent to which an investment is profitable, especially in

    relation to other investments. PERFORMANCE noun -
  4. the extent to which an investment is profitable, especially in

    relation to other investments. PERFORMANCE noun - app apps usable
  5. DELAY USER REACTION 0 - 100 ms Instant 100 -

    300 ms Slight perceptable delay 300 - 1000 ms Task focus, perceptable delay 1000+ ms Mental context swtich 10,000+ ms I’ll come back later! User perception-reaction times http://goo.gl/7noSwL
  6. 0 150 300 450 600 Time 3x faster CHROME 0

    750 1500 2250 3000 Time 4x faster iOS SAFARI
  7. <html> <head> </head> <body ... </body> </html> <link rel="import" href="elements.html">

    HTML Imports do not block parsing but they do block page rendering.
  8. <html> <head> </head> <body ... </body> </html> <link rel="import" href="elements.html"

    async> Make it async if you don’t want to block rendering.
  9. Dynamically load an HTML Import function importPage(url) { return new

    Promise(function(resolve, reject) { resolve(e.target.import); }, reject); }); }; Polymer.Base.importHref(url, function(e) { importPage(‘/path/to/import.html’).then(doStuff);
  10. Dynamically load an HTML Import function resolve }; Dynamically creates

    and loads <link rel=“import” href=“/path/to/ import.html”> Polymer.Base.importHref(url, function(e) { importPage
  11. Dynamically load an HTML Import function importPage(url) { return new

    Promise(function(resolve, reject) { resolve(e.target.import); }, reject); }); }; Dynamically creates and loads <link rel=“import” href=“/path/to/ import.html”> Polymer.Base.importHref(url, function(e) { importPage(‘/path/to/import.html’).then(doStuff);
  12. <html> <head> <script src="webcomponents-lite.min.js" async></script> </head> <body unresolved> <paper-drawer-panel> ...

    </paper-drawer-panel> </body> </html> Declarative imports load faster <link rel="import" href="elements.html" async>
  13. <html> <head> </head> <body ... </body> </html> Declarative imports load

    faster <link rel="import" href="elements.html" async>
  14. <html> <head> <script src="webcomponents-lite.min.js" async></script> <link rel="import" href="elements.html" async> </head>

    <body> <paper-drawer-panel> ... </paper-drawer-panel> </body> </html> “Fast load skeleton” Async all the things. Control FOUC ourselves!
  15. <html> <head> <link rel="import" href="elements.html" async> </head> <body> <paper-drawer-panel> ...

    </paper-drawer-panel> </body> </html> <script src="webcomponents-lite.min.js" async></script>
  16. <html> <head> <link rel="import" href="elements.html" async> </head> <body> <paper-drawer-panel> ...

    </paper-drawer-panel> </body> </html> <script src=“app.js" async></script>
  17. var webComponentsSupported = ( 'registerElement' in document && 'import' in

    document.createElement('link') && 'content' in document.createElement('template')); Lazy load the web component polyfills
  18. var webComponentsSupported = ( 'registerElement' in document && 'import' in

    document.createElement('link') && 'content' in document.createElement('template')); if (!webComponentsSupported) { var script = document.createElement('script'); script.async = true; script.src = 'webcomponents-lite.min.js'; script.onload = finishLazyLoading; document.head.appendChild(script); } else { finishLazyLoading(); } Lazy load the web component polyfills
  19. ( DevTools emulation - Good 3G ) 1430ms first paint

    230ms first paint 6.2x faster OLD NEW Stock Ticker App
  20. ( DevTools emulation - Good 3G ) 1430ms first paint

    230ms first paint 6.2x faster OLD NEW Stock Ticker App
  21. <html> <head> </head> <paper-drawer-panel> ... </paper-drawer-panel> </body> </html> <body> <!--

    removed unresolved attr. --> <link rel="import" href="elements.html" async>
  22. <html> <head> </head> ... </body> </html> <body> <!-- removed unresolved

    attr. --> <link rel="import" href="elements.html" async>
  23. <html> <head> <link rel="import" href="elements.html" async> <style> paper-header-panel[drawer]:unresolved { display:

    none; } paper-toolbar.tall:unresolved { height: 192px; background: #42A5F5; } </style> </head> <body> <paper-drawer-panel> <paper-header-panel drawer>...</paper-header-panel> <paper-header-panel mode="cover" main> <paper-toolbar class="tall"> ... </paper-toolbar> </paper-header-panel> </paper-drawer-panel> </body> </html>
  24. <html> <head> <link rel="import" href="elements.html" async> <style> paper-header-panel[drawer]:unresolved { display:

    none; } paper-toolbar.tall:unresolved { height: 192px; background: #42A5F5; } </style> </head> <body> <paper-drawer-panel> <paper-header-panel drawer>...</paper-header-panel> <paper-header-panel mode="cover" main> <paper-toolbar class="tall"> ... </paper-toolbar> </paper-header-panel> </paper-drawer-panel> </body> </html>
  25. <html> <head> <link rel="import" href="elements.html" async> <style> paper-header-panel[drawer]:unresolved { display:

    none; } paper-toolbar.tall:unresolved { height: 192px; background: #42A5F5; } </style> </head> <body> <paper-drawer-panel> <paper-header-panel drawer>...</paper-header-panel> <paper-header-panel mode="cover" main> <paper-toolbar class="tall"> ... </paper-toolbar> </paper-header-panel> </paper-drawer-panel> </body> </html>
  26. <html> <head> <link rel="import" href="elements.html" async> <style> paper-header-panel[drawer]:unresolved { display:

    none; } paper-toolbar.tall:unresolved { height: 192px; background: #42A5F5; } </style> </head> <body> <paper-drawer-panel> <paper-header-panel drawer>...</paper-header-panel> <paper-header-panel mode="cover" main> <paper-toolbar class="tall"> ... </paper-toolbar> </paper-header-panel> </paper-drawer-panel> </body> </html>
  27. <html> <head> <link rel="import" href="elements.html" async> <style> paper-header-panel[drawer]:unresolved { display:

    none; } paper-toolbar.tall:unresolved { height: 192px; background: #42A5F5; } </style> </head> <body> <paper-drawer-panel> <paper-header-panel drawer>...</paper-header-panel> <paper-header-panel mode="cover" main> <paper-toolbar class="tall"> ... </paper-toolbar> </paper-header-panel> </paper-drawer-panel> </body> </html>
  28. @ebidel @polymer #polymersummit PolyMail 1.0 ‣ ES6 classes ‣ Async

    Imports, lazy loaded polyfills ‣ Add to Homescreen ‣ <meta name="theme-color"> ‣ Offline Source: github.com/ebidel/polymer-gmail poly-mail.appspot.com
  29. @ebidel @polymer #polymersummit PolyMail 1.0 ‣ ES6 classes ‣ Async

    Imports, lazy loaded polyfills ‣ Add to Homescreen ‣ <meta name="theme-color"> ‣ Offline Source: github.com/ebidel/polymer-gmail poly-mail.appspot.com
  30. 1144 speed index Cable connection ( DESKTOP ) webpagetest.org results:

    https://goo.gl/LY0sxj 589ms first paint 1s total load
  31. 3G Fast connection ( MOTO G ) 1.66s first paint

    webpagetest.org results: https://goo.gl/LY0sxj
  32. 3G Fast connection ( MOTO G ) 1.66s first paint

    ~ 7s total load webpagetest.org results: https://goo.gl/LY0sxj
  33. 3G Fast connection ( MOTO G ) 5614 speed index

    1.66s first paint ~ 7s total load webpagetest.org results: https://goo.gl/LY0sxj
  34. @ebidel @polymer #polymersummit Changes for fast load 1.Make HTML Imports

    2.Conditionally load the polyfills (or make 3.Manually control FOUC instead of using 4.Load critical “app shell”
  35. <script> window.Polymer = window.Polymer || {dom: ‘shadow'}; </script> <link rel="import"

    href="polymer.html"> Use native shadow DOM ( it’s faster )
  36. Yo, don’t jank those ripples <paper-button raised on-transitionend="_doNav"> <a href="/new"

    on-tap="_navAfterRipple">Add a new feature</a> </paper-button> <script> Polymer({ _navAfterRipple: function(e) { e.preventDefault(); this._href = Polymer.dom(e).localTarget.href; // save reference } _doNav: function(e) { var href = this._href; if (href) { this._href = null; // clear location.href = href; } } }); </script> Ripple jank on page navigations
  37. Yo, don’t jank those ripples <paper-button raised on-transitionend="_doNav"> <a href="/new"

    on-tap="_navAfterRipple">Add a new feature</a> </paper-button> <script> Polymer({ _navAfterRipple: function(e) { e.preventDefault(); this._href = Polymer.dom(e).localTarget.href; // save reference } _doNav: function(e) { var href = this._href; if (href) { this._href = null; // clear location.href = href; } } }); </script> Ripple jank on page navigations
  38. <style> my-element[selected] { border: 1px solid black; } </style> <my-element

    selected></my-element> properties: { selected: { type: Boolean, reflectToAttribute: true } } Minimize property reflection Use only for CSS selectors:
  39. @ebidel @polymer #polymersummit How DOM nodes do modern apps create?

    APP # NODES AT PAGE LOAD Maps 624 Contacts 713 Inbox 988 Calendar 968 Photos 1,106 Github 1,276 G+ 2,838 GMail 2,890
  40. Preload an Import <link rel="preload" href=“/path/to/import.html" as=“html”> Declarative var l

    = document.createElement('link'); l.rel = 'preload'; l.as = 'html'; l.href = '/path/to/import.html'; document.head.appendChild(l); Script preload what you’ll use HTTP header Link: </path/to/>; rel=preload; as=html
  41. @ebidel @polymer #polymersummit Polydev Chrome extension know the costs of

    your elements goo.gl/pgPeCE source: github.com/polymerlabs/polydev
  42. @ebidel @polymer #polymersummit Polymer perf bookmarklet know your app’s metrics

    ‣ Time to first paint & page load ‣ HTML Import load times ( main page ) ‣ # of custom element instances ‣ Element properties using goo.gl/vkJHWm