Slide 1

Slide 1 text

Ember Fest new | show | ask | jobs | submit 1. Building the Progressive Web App for HackerNews.io in Ember (hackernews.io) 1337 points by ivanvanderbyl 41 minutes ago | flag | hide | 18 comments

Slide 2

Slide 2 text

Ivan Vanderbyl @ivanderbyl Engineering Lead on Flood at Tricentis GmbH • flood.io • tricentis.com

Slide 3

Slide 3 text

Talk Outline ● Progressive Web App fundamentals ● HackerNews PWA ● Optimising the first view experience ● Tips and tricks

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

1. Hacker News Top Stories, New Stories, Show HN, Ask HN, Jobs & threaded Comments. 2. Each of these should use routing to enable shareability. 3. App must display 30 items per-page for story list views 4. App must be a Progressive Web App 5. App must score over a 90/100 using Lighthouse 6. App must aim to be interactive in under 5 seconds on a Moto G4 7. App must use the Application Shell pattern 8. App is responsive on desktop and mobile 9. App must do its best to work cross-browser

Slide 8

Slide 8 text

But first, what is a PWA?

Slide 9

Slide 9 text

Reliable: Load instantly and never show the Downasaur, even in uncertain network conditions. Fast: Respond quickly to user interactions with silky smooth animations and no janky scrolling. Engaging: Feel like a natural app on the device, with an immersive user experience. Progressive Web App https://developers.google.com/web/progressive-web-apps/

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

1. Site is served over HTTPS 2. Pages are responsive on tablets & mobile devices 3. All app URLs load while offline 4. Metadata provided for Add to Home screen 5. First load fast even on 3G 6. Site works cross-browser 7. Page transitions don't feel like they block on the network 8. Each page has a URL https://developers.google.com/web/progressive-web-apps/checklist

Slide 12

Slide 12 text

Progressive Ember App PEA

Slide 13

Slide 13 text

No content

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

Service Workers + Metadata ember-cli-service-worker + ember-web-app All app URLs load while offline Metadata provided for Add to Home screen Pages are responsive on tablets & mobile devices, Site works cross-browser Each page has a URL, Page transitions don't feel like they block on the network Postcss + Browser Targets ember-cli-postcss + autoprefixer Ember Routing

Slide 16

Slide 16 text

No content

Slide 17

Slide 17 text

No content

Slide 18

Slide 18 text

// app/models/item.js +export default Model.extend({ + by: attr(), + kids: attr(), + parent: attr(), + text: attr(), + time: attr(), + type: attr() +}); Building a working app, first pass // app/router.js Router.map(function() { + this.route('new'); + this.route('show'); + this.route('ask'); + this.route('jobs'); + this.route('about'); + this.route('item', { path: 'item/:item_id' }); });

Slide 19

Slide 19 text

// app/route/index.js + model() { + let config = { + databaseURL: 'https://hacker-news.firebaseio.com' + }; + firebase.initializeApp(config); + + return new RSVP.Promise((resolve) => { + firebase.database().ref('v0/topstories').once('value', (snap) => { + let ids = snap.val(); + + resolve(RSVP.all(ids.slice(0, 20).map((id, index) => { + return new RSVP.Promise((resolve) => { + firebase.database().ref(`v0/item/${id}`).once('value', (snap) => { + let item = snap.val(); + item.position = index + 1; + resolve(item); + }); + }); + }))); + }); + }); + },

Slide 20

Slide 20 text

The other components tl;dr ● Date formatting: moment.js ● Data loading: Ember Data + Firebase ● Service Workers ○ ember-cli-service-worker ○ ember-service-worker-asset-cache ○ Ember-service-worker-index ● App Manifest: ember-web-app

Slide 21

Slide 21 text

No content

Slide 22

Slide 22 text

The point at which layout has stabilized, key webfonts are visible, and the main thread is available enough to handle user input developers.google.com/web/tools/lighthouse/audits/time-to-interactive

Slide 23

Slide 23 text

Ember HN ? WebPageTest.com score for Time To Interactive “TTI” on a Moto G4/3G Connection — (Chrome with device emulation at 4x slowdown) React HN 4.1s

Slide 24

Slide 24 text

Ember HN 12.5s WebPageTest.com score for Time To Interactive “TTI” on a Moto G4/3G Connection — (Chrome with device emulation at 4x slowdown) React HN 4.1s

Slide 25

Slide 25 text

No content

Slide 26

Slide 26 text

No content

Slide 27

Slide 27 text

Ember HN 4.1s WebPageTest.com score for Time To Interactive “TTI” on a Moto G4/3G Connection — (Chrome with device emulation at 4x slowdown) React HN 4.1s

Slide 28

Slide 28 text

No content

Slide 29

Slide 29 text

No content

Slide 30

Slide 30 text

Ember jQuery Firebase Client Ember Data ???

Slide 31

Slide 31 text

GET vendor.js DNS Connect TLS Download Parse Compile Execute Time

Slide 32

Slide 32 text

How to remove jQuery: 1. Nullify vendorFiles.jquery 2. Delete “ember-ajax” from package.json 3. Install: ember-native-dom-event-dispatcher 4. Install ember-native-dom-helpers (for easier testing) 5. Install ember-fetch (to replace ember-ajax) 6. Optional: Run ember-native-dom-helpers-codemod which will fix all your codez.

Slide 33

Slide 33 text

GET https://node-hnapi.herokuapp.com/news [ { "id": 15426983, "title": "Thoughts on Microsoft's Time-Travel Debugger", "points": 63, "user": "JoshTriplett", "time": 1507437139, "time_ago": "4 hours ago", "comments_count": 19, "type": "link", "url": "http://robert.ocallahan.org/2017/10/thoughts-on-microsofts-time-travel.html", "domain": "robert.ocallahan.org" }, ... ]

Slide 34

Slide 34 text

import Ember from 'ember' import fetch from 'fetch' export default Ember.Route.extend({ apiHost: 'https://node-hnapi.herokuapp.com', page: 'news', model() { let pageUrl = `${this.get('apiHost')}/${this.get('page')}` return fetch(pageUrl).then(response => response.json()) }, setupController(controller, items) { controller.setProperties({ items }) } }

Slide 35

Slide 35 text

Interactive 6.8s Starts loading API

Slide 36

Slide 36 text

Fundamental Truths about browsers ● Single threaded execution ● Maximum 6-8 concurrent connections per domain on HTTP/1.1, virtually unlimited with H2. ● Resources have priority ● Script Streamer Thread ● Service Workers ● Tricks to change resource priority

Slide 37

Slide 37 text

Main Thread DNS Connect TLS Download Parse Compile Execute Time Normal load order Script Streamer Thread Main Thread DNS Connect TLS Download Parse Compile Execute With async / defer attribute

Slide 38

Slide 38 text

Using async with vendor.js vendor.js app.js Vendor.js + app.js ember install ember-cli-concat

Slide 39

Slide 39 text

Compiling and Parsing

Slide 40

Slide 40 text

Main Thread Connect TLS Download With rel=dns-prefetch meta link DNS Main Thread DNS Connect TLS Download Normal request Main Thread Connect TLS Download With rel=preconnect meta link DNS Main Thread Connect TLS Download With rel=preload or rel=prefetch meta link DNS

Slide 41

Slide 41 text

CSS is a render blocking resource. Get it to the client as soon and as quickly as possible to optimize the time to first render. https://developers.google.com/web/fundamentals/performance/critical-rendering-path/render-blocking-css

Slide 42

Slide 42 text

Reduce render blocking CSS and finish initial render earlier. ember-cli-critical COST ~450ms per stylesheet

Slide 43

Slide 43 text

Add a CDN (Content Delivery Network) A few good options: ● S3 + CloudFront (what we use) ● S3 + Cloudflare ● S3 + fastly ● Google Storage + Google CDN ● Firebase Hosting + Google CDN

Slide 44

Slide 44 text

Time To Interactive 4.08s webpagetest.com

Slide 45

Slide 45 text

Online Offline hackernews.io

Slide 46

Slide 46 text

Quick Recap 1. ember-cli-service-worker + ember-web-app 2. Use async and concat your assets, move parsing to separate Script Streamer Thread, 3. Preconnect or preload if possible 4. Inline your critical CSS 5. Inline service worker registration 6. Use a CDN 7. You can’t improve anything you can’t measure!

Slide 47

Slide 47 text

Looking forward... 1. Glimmer binary templates and other rendering improvements already give you TTI in ≤5s on mobile, 2. Glimmer SSR + Rehydration should reduce the amount of time SSR has on TTFB, 3. Future Ember should automatically remove unused modules during compilation, 4. Will future Ember drop jQuery? 5. Sprite maps might help reduce asset requests,

Slide 48

Slide 48 text

Ember HN 4.1s WebPageTest.com score for Time To Continuously Interactive “TTCI” on a Moto G4/3G Connection — (Chrome with device emulation at 4x slowdown) Glimmer HN 3.1s

Slide 49

Slide 49 text

Resources https://www.w3.org/TR/preload/ https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types https://medium.com/reloading/preload-prefetch-and-priorities-in-chrome-776165961bbf https://blog.chromium.org/2015/03/new-javascript-techniques-for-rapid.html https://github.com/stefanpenner/broccoli-concat-analyser https://developers.google.com/web/tools/lighthouse/audits/time-to-interactive https://hacks.mozilla.org/2017/09/building-the-dom-faster-speculative-parsing-async-defer-and-preload/ https://aerotwist.com/blog/when-everything-is-important-nothing-is/ https://medium.com/reloading/javascript-start-up-performance-69200f43b201 Ivan Vanderbyl @ivanderbyl