Slide 1

Slide 1 text

+Eric Bidelman @ebidel Polymer Performance Patterns Amsterdam

Slide 2

Slide 2 text

@ebidel github.com/ebidel/polymer-experiments

Slide 3

Slide 3 text

@ebidel @polymer #polymersummit optimizing load & first paint LOAD optimization tips for minimizing in-app jank RENDER future APIs for performance FUTURE

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

0 150 300 450 600 Time 3x faster CHROME 0 750 1500 2250 3000 Time 4x faster iOS SAFARI

Slide 11

Slide 11 text

LOAD optimizations

Slide 12

Slide 12 text

...

Slide 13

Slide 13 text

thistock.im WE CAN DO BETTER.

Slide 14

Slide 14 text

Slide 15

Slide 15 text

Slide 16

Slide 16 text

Slide 17

Slide 17 text

HTML Imports do not block parsing but they do block page rendering.

Slide 18

Slide 18 text

Slide 19

Slide 19 text

Make it async if you don’t want to block rendering.

Slide 20

Slide 20 text

HTML IMPORTS

Slide 21

Slide 21 text

var foo = document.createElement(’x-foo‘); foo.bar = {baz: true}; Why are HTML Imports not async by default?

Slide 22

Slide 22 text

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);

Slide 23

Slide 23 text

Dynamically load an HTML Import function resolve }; Dynamically creates and loads Polymer.Base.importHref(url, function(e) { importPage

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

... Declarative imports load faster

Slide 26

Slide 26 text

Declarative imports load faster

Slide 27

Slide 27 text

unresolved

Slide 28

Slide 28 text

Slide 29

Slide 29 text

...

Slide 30

Slide 30 text

... “Fast load skeleton” Async all the things. Control FOUC ourselves!

Slide 31

Slide 31 text

thistock.im WE CAN DO BETTER.

Slide 32

Slide 32 text

...

Slide 33

Slide 33 text

Slide 34

Slide 34 text

...

Slide 35

Slide 35 text

...

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

No content

Slide 41

Slide 41 text

No content

Slide 42

Slide 42 text

ASYNC

Slide 43

Slide 43 text

ASYNC

Slide 44

Slide 44 text

SYNC

Slide 45

Slide 45 text

SYNC

Slide 46

Slide 46 text

FOUC thistock.im f

Slide 47

Slide 47 text

...

Slide 48

Slide 48 text

...

Slide 49

Slide 49 text

CUSTOM ELEMENTS PROGRESSIVELY ENHANCED MARKUP ARE ASIDE

Slide 50

Slide 50 text

document.registerElement( ‘x-foo’, {prototype: … ASIDE

Slide 51

Slide 51 text

App shell = progressively enhanced HTML

Slide 52

Slide 52 text

App shell = progressively enhanced HTML

Slide 53

Slide 53 text

paper-header-panel[drawer]:unresolved { display: none; } paper-toolbar.tall:unresolved { height: 192px; background: #42A5F5; } ... ...

Slide 54

Slide 54 text

paper-header-panel[drawer]:unresolved { display: none; } paper-toolbar.tall:unresolved { height: 192px; background: #42A5F5; } ... ...

Slide 55

Slide 55 text

paper-header-panel[drawer]:unresolved { display: none; } paper-toolbar.tall:unresolved { height: 192px; background: #42A5F5; } ... ...

Slide 56

Slide 56 text

paper-header-panel[drawer]:unresolved { display: none; } paper-toolbar.tall:unresolved { height: 192px; background: #42A5F5; } ... ...

Slide 57

Slide 57 text

paper-header-panel[drawer]:unresolved { display: none; } paper-toolbar.tall:unresolved { height: 192px; background: #42A5F5; } ... ...

Slide 58

Slide 58 text

No content

Slide 59

Slide 59 text

No content

Slide 60

Slide 60 text

App shell reduced first paint.

Slide 61

Slide 61 text

-100ms App shell reduced first paint.

Slide 62

Slide 62 text

@ebidel @polymer #polymersummit PolyMail 1.0 ‣ ES6 classes ‣ Async Imports, lazy loaded polyfills ‣ Add to Homescreen ‣ ‣ Offline Source: github.com/ebidel/polymer-gmail poly-mail.appspot.com

Slide 63

Slide 63 text

@ebidel @polymer #polymersummit PolyMail 1.0 ‣ ES6 classes ‣ Async Imports, lazy loaded polyfills ‣ Add to Homescreen ‣ ‣ Offline Source: github.com/ebidel/polymer-gmail poly-mail.appspot.com

Slide 64

Slide 64 text

Cable connection ( DESKTOP ) webpagetest.org results: https://goo.gl/LY0sxj

Slide 65

Slide 65 text

Cable connection ( DESKTOP ) webpagetest.org results: https://goo.gl/LY0sxj 589ms first paint

Slide 66

Slide 66 text

Cable connection ( DESKTOP ) webpagetest.org results: https://goo.gl/LY0sxj 589ms first paint 1s total load

Slide 67

Slide 67 text

1144 speed index Cable connection ( DESKTOP ) webpagetest.org results: https://goo.gl/LY0sxj 589ms first paint 1s total load

Slide 68

Slide 68 text

3G Fast connection ( MOTO G ) webpagetest.org results: https://goo.gl/LY0sxj

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

No content

Slide 73

Slide 73 text

No content

Slide 74

Slide 74 text

PolyMail poly-mail.appspot.com

Slide 75

Slide 75 text

PolyMail poly-mail.appspot.com

Slide 76

Slide 76 text

@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”

Slide 77

Slide 77 text

RENDER optimizations

Slide 78

Slide 78 text

QUICK TIPS for

Slide 79

Slide 79 text

window.Polymer = window.Polymer || {dom: ‘shadow'}; Use native shadow DOM ( it’s faster )

Slide 80

Slide 80 text

Use touch events instead of click events

Slide 81

Slide 81 text

Yo, don’t jank those ripples Add a new feature 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; } } }); Ripple jank on page navigations

Slide 82

Slide 82 text

Yo, don’t jank those ripples Add a new feature 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; } } }); Ripple jank on page navigations

Slide 83

Slide 83 text

properties: { selected: { type: Boolean, reflectToAttribute: true } } Minimize property reflection

Slide 84

Slide 84 text

properties: { selected: { type: Boolean, reflectToAttribute: true } } Minimize property reflection

Slide 85

Slide 85 text

my-element[selected] { border: 1px solid black; } properties: { selected: { type: Boolean, reflectToAttribute: true } } Minimize property reflection Use only for CSS selectors:

Slide 86

Slide 86 text

No content

Slide 87

Slide 87 text

DON’T create 1000s of element instances

Slide 88

Slide 88 text

DON’T create 1000s of element instances DO lazy render elements

Slide 89

Slide 89 text

@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

Slide 90

Slide 90 text

chromestatus.com case study

Slide 91

Slide 91 text

Clicking feature panel reveals more

Slide 92

Slide 92 text

Clicking feature panel reveals more

Slide 93

Slide 93 text

1817 icons!! Clicking feature panel reveals more

Slide 94

Slide 94 text

thistock.im WE CAN DO BETTER.

Slide 95

Slide 95 text

Generate fewer elements

Slide 96

Slide 96 text


Slide 97

Slide 97 text


FAIL

Slide 98

Slide 98 text

Reducing elements on chromestatus.com

Slide 99

Slide 99 text

1227ms 383ms 4,400 elements Reducing elements on chromestatus.com 53 elements load load

Slide 100

Slide 100 text

FUTURE optimizations

Slide 101

Slide 101 text

Preload an Import preload what you’ll use

Slide 102

Slide 102 text

Preload an Import Declarative preload what you’ll use

Slide 103

Slide 103 text

Preload an Import Declarative preload what you’ll use HTTP header Link: ; rel=preload; as=html

Slide 104

Slide 104 text

Preload an Import 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: ; rel=preload; as=html

Slide 105

Slide 105 text

HTTP 2.0 push critical resources

Slide 106

Slide 106 text

http20-experiment.appspot.com goo.gl/mnQ5rN WebPageTest results: code: github.com/GoogleChrome/http2push-gae

Slide 107

Slide 107 text

http20-experiment.appspot.com HTTP2 push reduced load by 300ms! goo.gl/mnQ5rN WebPageTest results: code: github.com/GoogleChrome/http2push-gae

Slide 108

Slide 108 text

If you can’t measure it, you can’t improve it.

Slide 109

Slide 109 text

@ebidel @polymer #polymersummit Polydev Chrome extension know the costs of your elements goo.gl/pgPeCE source: github.com/polymerlabs/polydev

Slide 110

Slide 110 text

@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

Slide 111

Slide 111 text

+Eric Bidelman @ebidel thistock.im code: github.com/ebidel/polymer-experiments