Progressive Web Apps

Show Up for Offline First. Stay for the Killer Performance Boost.

by Jason Lengstorf
@jlengstorf | jason@lengstorf.com

Slides: git.io/pwa-perf

How much does your websitecost?

We measure cost in seconds:

seconds to load popular websites

Source: Time To Interactive from Lighthouse. I ran the test twice and took the faster time.

Time isn’t the only cost

Mobile Data Can Be Expensive

$2.05/MB

Price in USD for a US-based AT&T customer traveling internationally.

Websites are getting larger

website size from 2012 to 2017

Source: HTTP Archive

Would you pay $10
to load this website?

cost in USD to load popular websites

Also: loading huge sites on mobile just sucks

Chrome’s no connection screen

...and what if the connection is flaky?

We Can Fix It!

(And it doesn’t mean starting from scratch.)

The Solution:

Progressive Web Apps

With Progressive Web Apps (PWAs), we can:

  1. Significantly decrease the amount of data transferred on each page load
  2. Improve the experience for users on unreliable connections
boooooring

Booooooooooooooring!

“That’s not my target market.”

eat your veggies

Can’t we just ignore PWAs?

Nope

Here’s why:

  • PWAs decrease load times on all connections
  • Perceived load times improve
  • Stability improves for mobile users everywhere
  • User experience is better all around

Real-World Examples

lengstorf.com

lengstorf.com
  • Cold: ~3s (477 KB)
  • Warm: ~1s (0.5 KB)

csswizardry.com

csswizardry.com
  • Cold: ~1.5s (339 KB)
  • Warm: ~1s (1.1 KB)
bacon

That sounds significantly more awesome than eating vegetables.

What makes a PWA?

  • Web-based application
  • Provides a Web Manifest
  • Registers a Service Worker

Web Manifest

{
  "short_name": "AirHorner",
  "name": "Kinlan's AirHorner of Infamy",
  "icons": [{
    "src": "launcher-icon-1x.png",
    "type": "image/png",
    "sizes": "48x48"
  }],
  "start_url": "index.html?launcher=true"
}

bit.ly/pwa-manifest

Service Worker

self.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open('mysite-v1').then(function(cache) {
      cache.addAll(
        // add non-critical assets
      );
      return cache.addAll(
        // add critical assets
      );
    })
  );
});

bit.ly/offline-cookbook

What Service Workers do:

  • Cache critical assets when first installed
  • Precache non-critical assets in the background
  • Act as a proxy for all outgoing requests
  • Skip server & provide cached assets if possible

This allows Progressive Web Apps to
load offline on subsequent visits

But I have some bad news

Service Workers alone aren’t enough to make our apps blazing fast

(They don’t help at all on the first visit.)

To make JS-powered apps really fast, we should:

  • Implement route-based code splitting
  • Use HTTP/2 to serve assets
  • Set up background prefetching
  • Lazy load any non-critical assets

You may have heard this called the PRPL Pattern

Addy Osmani

Addy Osmani
bit.ly/prpl-pattern

Push

Render

Pre-cache

Lazy-load

To make JS-powered apps really fast, we should:

  • Implement route-based code splitting
  • Use HTTP/2 to serve assets
  • Set up background prefetching
  • Lazy load any non-critical assets
  • Eliminate all unused code
  • Use server-side rendering to generate static files

That’s a lot of things.

Kanye WTF.

Route-based code splitting:

  • Creates smaller bundles for specific routes
  • Creates shared bundles for common code
  • Allows loading only what’s really needed
App build bundles.

Source: Google Developers

Code splitting with Webpack:

  • Use import() to identify chunks
  • in Webpack 4, use optimization.splitChunks
  • In Webpack 3 or earlier, use CommonsChunkPlugin

Totally True Internet Fact

Summoning Sean Larkin

If you say “Webpack” 3 times, Sean Larkin will appear.

Serving assets with HTTP/2:

  • Allows multiplexed downloads
  • Provides push capabilities to minimize requests

Asset prefetching:

  • Assumes which routes the user will load next
  • Loads the assets for those routes in the background
  • Makes loading the next route almost instantaneous

Lazy loading assets:

  • Prevents images from blocking the initial load
  • Only requests them once they’re in the viewport

“Won’t lazy loading make the page jump?”

Page jumping while images load.

Source: bit.ly/2GayxFF

We can fix it!

Lazy loading with loading animation

Lazy loading with animation.

Source: responsive-lazyload.js

Lazy loading with the blur-up technique

Lazy loading with animation.

Source: medium.com

Lazy loading with traced SVG

Lazy loading with traced SVG.

Source: Gatsby.js

Lazy loading with simplified polygons

Lazy loading with SVG polygons.

Source: Gatsby.js

Eliminating unused code:

  • Reduces the overall size of the codebase
  • Helps avoid module bloat
Heaviest objects in the universe.

Source: Reddit?

How to remove unused code:

  • Use Uglify.js to remove dead code
  • Enable tree-shaking with ES modules
  • Analyze your bundle output
Webpack Bundle Analyzer.

Source: Webpack Bundle Analyzer

Serving only static assets:

  • Removes the need for runtime server rendering
  • Eliminates an extra HTTP round-trip
  • Improves cacheability

😱 Server-side rendering?! 😱

But it’s hard!

☹️

Loading 1MB of blocking JavaScript before anything can be displayed

😐

Showing a loading spinner while the framework initializes

😊

Server-side rendering so no JavaScript is required to initially display the page

“But static files won’t work for my app!”

Yes they will. Probably.

If all of this seems a little overwhelming I totally understand

Performance is hard

We can get most of these performance improvements for free

Open source is awesome. 🎉💕

How to improve web performance for free:

  • sw-precache and/or sw-toolbox
  • Next.js + Now
  • Gatsby.js + Netlify

sw-precache / sw-toolbox

  • Helper libs from the Google team
  • Sets up sane defaults for Service Workers
  • Requires manual configuration

Next.js

  • Framework for server-side rendering React
  • Automatic code-splitting
  • Requires a server

Now

  • Hosting for Node, static, and Docker applications
  • Purpose-built for Next.js apps
  • Amazingly simple CLI tooling

Gatsby.js

  • Framework for building React apps
  • Compiles to static files
  • Add Service Worker + Web Manifest with plugins
  • Generated apps are unbelievably fast

Netlify

  • Deployment and hosting for static sites
  • Powerful Git workflow integrations
  • Automatic, free SSL generation

Let’s Recap

PWAs are the future

🔥🔥🔥

Blazing fast apps need fine tuning️

PWA Performance Checklist:

✅ Add a Service Worker
✅ Create a Web Manifest
✅ Follow the PRPL Pattern
✅ Remove unused code
✅ Generate static files

Use frameworks to automate perf best practices

  • sw-precache and sw-toolbox
  • Next.js + Now
  • Gatsby.js + Netlify

Spending time on performance is good for everyone.

  • Lower cost to pay-per-MB users
  • Better experience on mobile and low-powered devices
  • Improved reliability on spotty connections
  • Apps feel more “solid” on all devices
  • Repeat visitors see damn-near instant page loads
applause.

When we focus on performance everybody wins

Thanks!

Jason Lengstorf

Jason Lengstorf
Follow me on Twitter: @jlengstorf

Resources

  1. Jake Archibald’s Offline Cookbook
  2. Lighthouse
  3. sw-precache
  4. sw-toolbox
  5. responsive-lazyload.js
  6. Webpack
  7. Gatsby
  8. Netlify
  9. Next.js
  10. Now
  11. PRPL Pattern

Tweet: @jlengstorf #PerfMatters