Slide 1

Slide 1 text

Instant Loading Making the web competitive on mobile PayPal, Oct 17’ @addyosmani

Slide 2

Slide 2 text

My desk

Slide 3

Slide 3 text

Improving performance is a journey. Lots of small changes can lead to big gains.

Slide 4

Slide 4 text

1. JavaScript Frameworks 2. Large JavaScript Bundles 3. PRPL Pattern 4. 5. 6. Reduction in HTTP Requests 7. Service Workers 8. Image Compression TOPICS

Slide 5

Slide 5 text

loading is a user journey with many disparate expectations you’ve probably heard to REDUCE DNS LOOKUPS, REDUCE ROUND-TRIP TIMES, MINIMIZE REDIRECTS, ELIMINATE UNN

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

USERS LOOK FOR VISUAL FEEDBACK TO reassure them everything is working as expected.

Slide 8

Slide 8 text

first Interactive consistently Interactive

Slide 9

Slide 9 text

Time to Interactive <5s on an average mobile device over 3G *2s on repeat-load a:er Service Worker registered GOAL

Slide 10

Slide 10 text

19s 16s 420KB JavaScript Startup Performance, Double-Click Mobile Speed Matters report & the HTTP Archive The average web page on mobile in 2017 UNTIL INTERACTIVE FULLY LOADED JAVASCRIPT

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

No content

Slide 14

Slide 14 text

PERFORMANCE JavaScript Start-up Start-up o o V8 Runtime Call Stats

Slide 15

Slide 15 text

1MB script (250KB minified) JS Parse Time On Mobile

Slide 16

Slide 16 text

No content

Slide 17

Slide 17 text

about:inspect in Chrome DevTools

Slide 18

Slide 18 text

No content

Slide 19

Slide 19 text

No content

Slide 20

Slide 20 text

No content

Slide 21

Slide 21 text

Apple A11 Arm64 SOC in iPhone8 benchmarks faster than x86 in Macbook Pro

Slide 22

Slide 22 text

webpagetest.org/easy Moto G4 + 3G

Slide 23

Slide 23 text

A closer look at PayPal.com

Slide 24

Slide 24 text

No content

Slide 25

Slide 25 text

Opportunities to dns-prefetch more?

Slide 26

Slide 26 text

resources for the next navigation

Slide 27

Slide 27 text

Authenticated PayPal.com

Slide 28

Slide 28 text

Ideally, test on real (average) mobile hardware. CPU, GPU, cores, network packet loss can all introduce variance.

Slide 29

Slide 29 text

16 seconds in before First Paint

Slide 30

Slide 30 text

20 seconds before we see First Meaningful Paint

Slide 31

Slide 31 text

30 seconds before the page is Continuously Interactive

Slide 32

Slide 32 text

Network bound for 3+ seconds

Slide 33

Slide 33 text

14s just fetching resources

Slide 34

Slide 34 text

11 seconds in idle time (time when the browser is waiting on the CPU or GPU to do some processing)

Slide 35

Slide 35 text

~6 seconds spent just in parsing & compiling JavaScript Party on the main thread

Slide 36

Slide 36 text

No content

Slide 37

Slide 37 text

By default, React includes many helpful warnings. These warnings are very useful in development. However, they make React larger and slower so you should make sure to use the production version when you deploy the app.

Slide 38

Slide 38 text

No content

Slide 39

Slide 39 text

50% of script immediately loaded is unused

Slide 40

Slide 40 text

Use babel-preset-env to only transpile code for browsers that need it { "presets": [ ["env", { "targets": { "browsers": ["last 2 versions"] } }] ] } Only transpile what you need with

Slide 41

Slide 41 text

Many sites optimize for the Lowest Common Denominator Most users end up being deployed ES2015 polyfills

Slide 42

Slide 42 text

Deploying ES2015+ JavaScript in 2017 babel-preset-env +

Slide 43

Slide 43 text

Deploying ES2015+ JavaScript in 2017 main-legacy.js main.js

Slide 44

Slide 44 text

Switching to MozJPEG could save 60% on bytes

Slide 45

Slide 45 text

No content

Slide 46

Slide 46 text

Continuous Performance Tracking

Slide 47

Slide 47 text

No content

Slide 48

Slide 48 text

Code-splitting // Defines a “split-point” for a separate bundle require.ensure([], () => { const profile = require('./UserProfile', cb); }); import('./UserProfile') .then(loadRoute(cb)) .catch(errorLoading) Webpack 2+ Webpack 1 Also see Splittable, Closure Compiler or Browserify

Slide 49

Slide 49 text

Minify _everything_ Babelified ES5 w/Uglify ES2015+ with Babili css-loader + minimize:true Code-splitting Dynamic import() Route-based chunking Tree-shaking Webpack 2+ with Uglify RollUp DCE w/ Closure Compiler Optimize “Vendor” libs NODE_ENV=production CommonsChunk + HashedModuleIdsPlugin() Transpile less code babel-preset-env + modules:false Browserlist useBuiltIns: true Scope Hoisting: Webpack 3 RollUp Strip unused Lodash modules lodash-webpack-plugin babel-plugin-lodash Fewer Moment.js locales ContextReplacementPlugin()

Slide 50

Slide 50 text

workflow

Slide 51

Slide 51 text

No content

Slide 52

Slide 52 text

No content

Slide 53

Slide 53 text

No content

Slide 54

Slide 54 text

No content

Slide 55

Slide 55 text

No content

Slide 56

Slide 56 text

No content

Slide 57

Slide 57 text

No content

Slide 58

Slide 58 text

No content

Slide 59

Slide 59 text

No content

Slide 60

Slide 60 text

No content

Slide 61

Slide 61 text

No content

Slide 62

Slide 62 text

No content

Slide 63

Slide 63 text

No content

Slide 64

Slide 64 text

No content

Slide 65

Slide 65 text

Webpack support for Pure Modules

Slide 66

Slide 66 text

No content

Slide 67

Slide 67 text

No content

Slide 68

Slide 68 text

Plenty of lightweight options for mobile Lower total cost on size + parse times from the get-go

Slide 69

Slide 69 text

Preact on Lyft

Slide 70

Slide 70 text

Preact on Uber

Slide 71

Slide 71 text

No content

Slide 72

Slide 72 text

No content

Slide 73

Slide 73 text

No content

Slide 74

Slide 74 text

No content

Slide 75

Slide 75 text

Byte savings @

Slide 76

Slide 76 text

Brotli Display Ads from Google now served using Brotli compression! 40% Data-savings up to 15% in aggregate over gzip https://developers.googleblog.com/

Slide 77

Slide 77 text

Brotli Improved load time by 7% in India & 4% U.S bit.ly/linkedin-brotli Decreased the size of static assets by 20% bit.ly/dropbox-brotli 17% improvement for largest JS bundles bit.ly/certsimple-brotli 1.5 petabytes (million gigs) saved a day bit.ly/playstore-brotli

Slide 78

Slide 78 text

WebP 30% smaller than JPEG 25% smaller than PNG

Slide 79

Slide 79 text

WebP bit.ly/webp-format Serving over 43B image requests a day 25-30% savings for WebP on average (26% lossless) Data Saver + Web Store

Slide 80

Slide 80 text

WebP Conversion XNConvert Windows/Mac/Linux Can convert in batch Supports most formats Alternatively: imagemin Pixelmator ImageMagick GIMP Leptonica

Slide 81

Slide 81 text

WebP Serving Or use the Accept header + .htaccess to serve WebP if a browser supports it and it exists on disk.

Slide 82

Slide 82 text

Service Workers Inbox by Gmail 10% improvement in Time-to-Interactive

Slide 83

Slide 83 text

No content

Slide 84

Slide 84 text

HTTP Caching Checklist Use consistent URLs and minimize resource churn Provide a validation token (ETag) to avoid transferring unchanged bytes Identify resources that can be cached by intermediaries (like CDNs) Determine the optimal cache lifetime of resources (max-age) Consider a Service Worker for more control over your repeat visit caching 1. 2. 3. 4. 5. bit.ly/caching-checklist

Slide 85

Slide 85 text

No content

Slide 86

Slide 86 text

Let’s hack

Slide 87

Slide 87 text

ResourceLoadPriorityVeryHigh

Slide 88

Slide 88 text

No content

Slide 89

Slide 89 text

No content

Slide 90

Slide 90 text

ResourceLoadPriorityVeryHigh

Slide 91

Slide 91 text

PayPal-inium

Slide 92

Slide 92 text

Original Everything is high priority Preloaded JS Preloaded CSS + fonts

Slide 93

Slide 93 text

Original Everything is high priority JS + CSS is high priority CSS + fonts are high prio

Slide 94

Slide 94 text

No content

Slide 95

Slide 95 text

No content

Slide 96

Slide 96 text

No content

Slide 97

Slide 97 text

No content

Slide 98

Slide 98 text

No content

Slide 99

Slide 99 text

.. Link: 1.js; rel="preload"; as="script"

Slide 100

Slide 100 text

No content

Slide 101

Slide 101 text

No content

Slide 102

Slide 102 text

No content

Slide 103

Slide 103 text

No content

Slide 104

Slide 104 text

bit.ly/prpl-paFern PRPL bit.ly/prpl-paFern

Slide 105

Slide 105 text

HTTP/2 with 3G

Slide 106

Slide 106 text

HTTP/2 + preload with 3G

Slide 107

Slide 107 text

No content

Slide 108

Slide 108 text

No content

Slide 109

Slide 109 text

Express + HTTP/2 Push Headers const express = require('express'), let app = express(); app .use('/js', express.static('js')) .get('/', function (req, res) { res.set('Link', ` ; rel=preload; as='style', ; rel=preload; as='script', ; rel=preload; as='script'`)

Slide 110

Slide 110 text

No content

Slide 111

Slide 111 text

No content

Slide 112

Slide 112 text

No content

Slide 113

Slide 113 text

Alternatively: Track cache content using cookies if (supports_http2() && !http_cached('/app.js')) { header('link:; rel=preload; as=script’); setcookie('/app.js', 'is-cached', 0, '/'); }

Slide 114

Slide 114 text

Alternatively: Track cache content using cookies function http_cached($filename) { if ('is-cached' === $_COOKIE[$filename]) { return true; } else { return false; } } Try CASPer

Slide 115

Slide 115 text

No content

Slide 116

Slide 116 text

Repeat visit with Service Worker

Slide 117

Slide 117 text

Next: Differential Serving based on browser compatibility? HTTP/1 works better when resources are concatenated (bundled) HTTP/2 works better when resources are more granular (unbundled) Serve an unbundled build for server/browser combinations supporting HTTP/2. Trigger delivery with or HTTP/2 Push Serve a bundled build to minimize round-trips to get the app running on server/browser combinations that don't support HTTP/2 Push

Slide 118

Slide 118 text

Debugging: HTTP/2 Server Push in DevTools

Slide 119

Slide 119 text

Debugging: HTTP/2 Server Push in DevTools

Slide 120

Slide 120 text

HTTP/2 Server Push Rules Of Thumb Push just enough resources to fill idle network time, and no more. Push resources in evaluation-dependence order. Consider using strategies to track the client-side cache. Use the right cookies when pushing resources. Use server push to fill the initial cwnd. Consider preload links to reveal remaining critical resources. 1. 2. 3. 4. 5. bit.ly/h2push PUSH

Slide 121

Slide 121 text

No content

Slide 122

Slide 122 text

PRPL In-A-Box Polymer App Toolbox PREACT CLI

Slide 123

Slide 123 text

No content

Slide 124

Slide 124 text

No content

Slide 125

Slide 125 text

No content

Slide 126

Slide 126 text

With Service Workers

Slide 127

Slide 127 text

With HTTP/2 Server Push

Slide 128

Slide 128 text

babel-preset-env + per-browser bundles

Slide 129

Slide 129 text

babel-preset-env + per-browser bundles

Slide 130

Slide 130 text

Twitter Lite

Slide 131

Slide 131 text

No content

Slide 132

Slide 132 text

Interactive in <5s on 3G

Slide 133

Slide 133 text

Can we get fast 3G numbers across the board or regular 3G?

Slide 134

Slide 134 text

PRPL Push / Preload

Slide 135

Slide 135 text

18% improvement

Slide 136

Slide 136 text

36% improvement

Slide 137

Slide 137 text

Render

Slide 138

Slide 138 text

HTML Streaming reduced TTFB by 30% (200ms), increasing time user’s spent in the app. Nicolas Gallagher, Technical lead for Twitter Lite

Slide 139

Slide 139 text

No content

Slide 140

Slide 140 text

4x improvement to render perf by using requestIdleCallback() to defer JS loading of images. Nicolas Gallagher, Technical lead for Twitter Lite

Slide 141

Slide 141 text

Heavy image decode Lower image decode

Slide 142

Slide 142 text

No content

Slide 143

Slide 143 text

Adapt intelligently H E I G H T Size appropriately WIDTH IMAGE DECODE Compress carefully Take care with tools Prioritize critical images HIGH LOW Lazy-load the rest Choose the right format Image Optimisation

Slide 144

Slide 144 text

images.guide

Slide 145

Slide 145 text

Data Saver Mode introduced up to 70% savings for Twitter Lite Do this with the browser using the Save-Data client hint

Slide 146

Slide 146 text

Precache

Slide 147

Slide 147 text

This is a headline Followed by a subhead This is body copy and it goes a little like this and Lorem ipsum dolor sit amet, consectetur adipiscing elit. This is body copy and it goes a little like this and Lorem ipsum dolor sit amet, consectetur adipiscing elit. Application Shell A skeleton representing the user interface that can be offline cached & instantly rendered on repeat visits.

Slide 148

Slide 148 text

Before Service Worker After Service Worker

Slide 149

Slide 149 text

Lazy-load

Slide 150

Slide 150 text

No content

Slide 151

Slide 151 text

Before code-splitting

Slide 152

Slide 152 text

webpack-web.config.js const plugins = [ // extract vendor and webpack's module manifest new webpack.optimize.CommonsChunkPlugin({ names: [ 'vendor', 'manifest' ], minChunks: Infinity }), // extract common modules from all the chunks (requires no 'name' property) new webpack.optimize.CommonsChunkPlugin({ async: true, children: true, minChunks: 4 }) ];

Slide 153

Slide 153 text

After code-splitting bit.ly/twitterlite-perf bit.ly/twitter-casestudy

Slide 154

Slide 154 text

No content

Slide 155

Slide 155 text

No content

Slide 156

Slide 156 text

https://meowni.ca/font-style-matcher/

Slide 157

Slide 157 text

https://www.zachleat.com/web/comprehensive-webfonts/ “Comprehensive Web Fonts”

Slide 158

Slide 158 text

No content

Slide 159

Slide 159 text

No content

Slide 160

Slide 160 text

Link: ; rel=preload; as=font; type='font/woff'

Slide 161

Slide 161 text

Heaviest use of rel=preload is for Web Fonts HTTPArchive

Slide 162

Slide 162 text

Preloading Web Fonts = 50% (1.2s) improvement in time-to-text-paint

Slide 163

Slide 163 text

Control font performance with font-display auto: uses whatever font display strategy the user-agent uses block: draws "invisible" text at first if the font is not loaded, but swaps the font face in as soon as it loads swap: draws text immediately with a fallback if the font face isn’t loaded, but swaps the font face in as soon as it loads fallback: font face is rendered with a fallback at first if it’s not loaded, but the font is swapped as soon as it loads optional: if the font face can’t be loaded quickly, just use the fallback Chrome 60

Slide 164

Slide 164 text

No content

Slide 165

Slide 165 text

No content

Slide 166

Slide 166 text

bit.ly/font-subsetting

Slide 167

Slide 167 text

https://fonts.googleapis.com/css?family=Inconsolata https://fonts.googleapis.com/css?family=Inconsolata&text=Hello Web Font Subsetting ~3KB ~880 bytes Supported by Google Fonts

Slide 168

Slide 168 text

/* Small subset, normal weight */ @font-face { font-family: whatever; src: url('reg-subset.woff') format('woff'); unicode-range: U+0-A0; font-weight: normal; } The browser can also handle subsetting! https://jakearchibald.com/2014/minimising-font-downloads/ /* Large subset, normal weight */ @font-face { font-family: whatever; src: url('reg-extended.woff') format('woff'); unicode-range: U+A0-FFFF; font-weight: normal; } Chrome 36 Firefox 44

Slide 169

Slide 169 text

CSS Font Loading API https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/webfont-optimization const font = new FontFace("Awesome Font", "url(/fonts/awesome.woff2)", { style: 'normal', unicodeRange: 'U+000-5FF', weight: '400' }); // don't wait for the render tree, initiate an immediate fetch! font.load().then(function() { // apply the font (which may re-render text and cause a page reflow) // after the font has finished downloading document.fonts.add(font); document.body.style.fontFamily = "Awesome Font, serif"; // OR... apply your own render strategy here... }); Chrome 35 Firefox 41

Slide 170

Slide 170 text

Use System Fonts when you can The fastest font is one that doesn’t need to load. Try font-display: optional; If a Web Font can’t load fast, load a fallback instead. If the Web Font is cached, it’ll get used the next time the user loads the page. Try to request Web Fonts with a higher priority If Web Fonts are a critical to your UX, preload them to minimize FOIT. Try subsetting to limit the range of Web Font characters needed Subsetting removes characters & Open-Type features from fonts, reducing file size. Google Fonts, TypeKit & Font Squirrel support it. Be careful with use. Try the CSS Font Loading API if you need more control Track font download progress & apply once fetched, manipulate font faces and override default lazy load behavior. S Have a Web Font Loading Strategy @addyosmani

Slide 171

Slide 171 text

No content

Slide 172

Slide 172 text

Streams API bit.ly/streams-ftw Progressive Loading: HTML

Slide 173

Slide 173 text

in body bit.ly/progressive-css Progressive Loading: CSS

Slide 174

Slide 174 text

in body bit.ly/progressive-css Progressive Loading: CSS … … … …

Slide 175

Slide 175 text

medium.com/reloading

Slide 176

Slide 176 text

No content

Slide 177

Slide 177 text

No content

Slide 178

Slide 178 text

No content

Slide 179

Slide 179 text

No content

Slide 180

Slide 180 text

No content

Slide 181

Slide 181 text

No content

Slide 182

Slide 182 text

No content

Slide 183

Slide 183 text

No content

Slide 184

Slide 184 text

No content

Slide 185

Slide 185 text

No content

Slide 186

Slide 186 text

No content

Slide 187

Slide 187 text

No content

Slide 188

Slide 188 text

No content

Slide 189

Slide 189 text

No content

Slide 190

Slide 190 text

No content

Slide 191

Slide 191 text

No content

Slide 192

Slide 192 text

No content

Slide 193

Slide 193 text

No content

Slide 194

Slide 194 text

No content

Slide 195

Slide 195 text

No content

Slide 196

Slide 196 text

https://medium.com/dev-channel/treebo-a-react-and-preact-progressive-web-app- performance-case-study-5e4f450d5299 next do webpack bundle analyzer

Slide 197

Slide 197 text

No content

Slide 198

Slide 198 text

No content