0% found this document useful (0 votes)
3 views21 pages

PWA Lecture Notes MIT

Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
3 views21 pages

PWA Lecture Notes MIT

Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd

6.

S191
Introduction to Modern Web Engineering

LECTURE NOTES

Progressive Web Applications


(PWAs)
Architecture · APIs · Deployment · Performance

FAST NUCES, Lahore


PRE-REQUISITES: HTML5, CSS3, JavaScript ES6+, HTTP fundamentals, basic [Link]. These notes
assume you can read JavaScript fluently.

LEARNING OBJECTIVES: By the end of these lectures you will be able to (1) architect a production-grade
PWA from scratch, (2) implement a service worker with multiple caching strategies, (3) make a web app
installable, (4) add push notifications and background sync, and (5) audit and optimize a PWA with
Lighthouse.
Lecture 14 — Foundations of Progressive
Web Apps
14.1 The Problem We Are Solving
Before we define PWA, I want you to sit with a problem. You are a developer in 2010. Your
company wants to reach users on mobile. You have two choices. You can build a native app —
meaning a separate codebase for iOS in Objective-C and another for Android in Java. That is two
teams, two codebases, two App Store review processes, two update pipelines. Or you can build a
mobile website — one codebase, but no offline support, no home screen icon, no push
notifications, and performance so poor that Google's own data shows 40% of users abandon a
mobile site that takes more than 3 seconds to load.

Neither option is satisfying. The web is open, linkable, and universal. Native apps are fast and
capable but siloed. Can we get the best of both worlds? That is the question PWAs answer.

💡 THINK ABOUT IT: What would it mean for a website to behave 'like an app'? Brainstorm
three characteristics before reading on.

14.2 A Brief History


The story of PWAs starts with three pivotal moments:

1. 2007 — Steve Jobs' original vision: At the first iPhone launch, Jobs described the iPhone
SDK as "the full Safari engine." His original vision for third-party iPhone apps was —
believe it or not — web apps. The native App Store came later, and the web took a back
seat.
2. 2015 — The term 'Progressive Web App' is coined: Engineers Alex Russell and Frances
Berriman at Google coined the term to describe websites that use new browser APIs to
deliver app-like experiences. They published a landmark blog post outlining the checklist of
characteristics.
3. 2018 — iOS adds PWA support: Apple added service worker support in Safari 11.1,
making PWAs viable across all major platforms. Desktop PWA support in Chrome arrived
the same year.

14.3 The Official Definition and Its Implications


Google defines a PWA as an application that is reliable, fast, and engaging. That definition
sounds like marketing copy. Let me give you the engineering definition: a PWA is a web application
that satisfies a specific checklist of technical criteria verified by the Lighthouse audit tool. Those
criteria fall into three groups:

Group 1 — Installability Requirements


• Served over HTTPS (or localhost for development)
• Has a valid Web App Manifest with at minimum: name, short_name, start_url, icons
(192px and 512px), and display mode
• Has a registered Service Worker with a fetch event handler

Group 2 — Performance Requirements


• First Contentful Paint (FCP) under 1.8 seconds on a simulated 4G connection
• Time to Interactive (TTI) under 3.9 seconds
• Speed Index under 3.4 seconds
• Total Blocking Time (TBT) under 200 milliseconds

Group 3 — UX Requirements
• Works on any screen size (responsive design with proper meta viewport tag)
• Has 'Add to Homescreen' functionality (installable)
• Splash screen on launch (via manifest background_color and icons)
• Custom theme color (via manifest theme_color)

🎯 EXAM FOCUS: Questions in the past three final exams have asked students to identify which
criteria a described app fails. Know this checklist cold.

14.4 Progressive Enhancement: The Core Philosophy


The word "Progressive" carries a specific technical meaning borrowed from a broader web design
philosophy called Progressive Enhancement (PE). PE states that you should build your
application in layers:

4. Base layer (HTML): semantic, accessible content that works in any browser, even a text
browser.
5. Enhancement layer (CSS): visual design layered on top. Older browsers get a plain
layout; modern ones get animations, grid, etc.
6. Capability layer (JavaScript): advanced interactivity only for browsers that support it.
For PWAs, the same layering applies. A browser with no service worker support gets a normal
website. A browser with service worker support gets offline caching. A browser that supports the
Web Push API gets notifications. No user is broken — they simply get progressively richer
experiences based on what their environment supports.

📝 LECTURE NOTE: This is why we do NOT use: 'if (!('serviceWorker' in navigator))


{ alert("Browser not supported"); }' — that breaks the base experience. We use feature detection to
enhance, never to block.
Lecture 15 — Service Workers: Deep Dive
15.1 What Is a Service Worker, Really?
A Service Worker (SW) is a JavaScript file that runs in a separate thread from your main page —
what the browser spec calls a worker context. Critically, it has no access to the DOM. It cannot
call [Link]. It cannot manipulate HTML elements. Its job is one thing: sitting
between your web page and the network, intercepting every fetch request and deciding what to
return.

Think of it as a programmable network proxy that lives inside the browser. Unlike a server-side
proxy (like NGINX), it runs entirely client-side — on the user's machine. Unlike a Web Worker
(which I will contrast shortly), it persists beyond the lifetime of the page.

Property Main Thread Web Worker Service Worker


DOM Access ✓ Full ✗ None ✗ None
Network Intercept ✓ Via fetch() ✓ Via fetch() ✓ Intercepts all
Lifespan Tab lifetime Tab lifetime Persists beyond tab
Push Messages ✗ No ✗ No ✓ Yes
Background Sync ✗ No ✗ No ✓ Yes
HTTPS Required ✗ No ✗ No ✓ Yes

15.2 The Service Worker Lifecycle


The SW lifecycle is one of the most important and most misunderstood topics in PWA
development. Many bugs come from developers not understanding what phase their SW is in.

Phase 1 — Registration
The page registers the SW with a single call:

if ('serviceWorker' in navigator) {
[Link]('/[Link]', { scope: '/' })
.then(reg => [Link]('SW registered:', [Link]))
.catch(err => [Link]('SW failed:', err));
}
Key points about registration: The scope parameter defines which URLs the SW controls. A SW at
/app/[Link] can only intercept requests starting with /app/ unless you explicitly change its scope with
a Service-Worker-Allowed response header. Registration is idempotent — calling register() on
every page load is fine and expected.

Phase 2 — Installation
Once registered, the browser downloads and parses the SW file, then fires the install event. This
is where you pre-cache your app shell — the static assets that form the skeleton of your
application:

const CACHE_NAME = 'app-shell-v1';


const SHELL_ASSETS = ['/', '/[Link]', '/[Link]', '/[Link]', '/[Link]'];

[Link]('install', event => {


[Link]( // keeps SW alive until promise resolves
[Link](CACHE_NAME)
.then(cache => [Link](SHELL_ASSETS))
.then(() => [Link]()) // activate immediately, no waiting
);
});

📝 LECTURE NOTE: [Link]() is critical. Without it, the browser might terminate the SW
before caching completes if the installation takes time.

Phase 3 — Waiting
After installation, the SW enters a waiting state if there is already an active SW controlling the
page. The new SW cannot activate until all tabs using the old version are closed. This prevents the
jarring situation of two different versions of the app running simultaneously (e.g., old JS with new
HTML).

[Link]() in the install event overrides this and forces immediate activation. Use it
cautiously — it means two versions may briefly coexist. Pair it with [Link]() in the activate
event for a complete takeover.

Phase 4 — Activation
The activate event fires when the SW takes control. This is where you clean up old caches from
previous versions:

[Link]('activate', event => {


const CURRENT_CACHES = [CACHE_NAME];
[Link](
[Link]().then(cacheNames => {
return [Link](
cacheNames
.filter(name => !CURRENT_CACHES.includes(name))
.map(name => [Link](name))
);
}).then(() => [Link]()) // take control of all open tabs
);
});

Phase 5 — The Fetch Event (The Heart of the SW)


Once active, the SW intercepts every network request from pages in its scope:

[Link]('fetch', event => {


[Link]( // override the browser's default network
request
[Link]([Link]) // check cache first
.then(cachedResponse => {
if (cachedResponse) return cachedResponse; // CACHE HIT
return fetch([Link]); // CACHE MISS → go to network
})
);
});

15.3 Caching Strategies — The Five Patterns


Caching is not one-size-fits-all. You choose a strategy per resource type based on how frequently
it changes and how important freshness is. Here are the five canonical strategies:

Strategy 1: Cache Only


Serve exclusively from cache. If not in cache, the request fails. Use for: versioned static assets
([Link], [Link]) that never change once deployed.

// Cache Only
[Link]('fetch', event => {
[Link]([Link]([Link]));
});

Strategy 2: Network Only


Always go to network. Identical to no service worker at all. Use for: analytics pings, payment
endpoints — anything that must be fresh and should not be intercepted.

Strategy 3: Cache First, Network Fallback


Serve from cache if available; otherwise fetch from network and cache the response. Use for: app
shell assets, fonts, images, logos. Produces the fastest repeat load times.

Strategy 4: Network First, Cache Fallback


Always try network first. If network fails (offline), serve from cache. Use for: frequently updated
content like news feeds, user dashboards, API responses.

// Network First
[Link]('fetch', event => {
[Link](
fetch([Link])
.then(networkResponse => {
const clone = [Link]();
[Link](CACHE_NAME).then(c => [Link]([Link], clone));
return networkResponse;
})
.catch(() => [Link]([Link])) // fallback to cache on failure
);
});

📝 LECTURE NOTE: Responses can only be read once (they are streams). Always clone()
before both reading and caching: [Link]().

Strategy 5: Stale While Revalidate


Serve from cache immediately (fast!), but simultaneously fetch a fresh version in the background.
On the next request, the fresh version is served. Use for: profile pages, social feeds, content
that needs to feel fast but should eventually be fresh.

// Stale While Revalidate


[Link]('fetch', event => {
[Link](
[Link](CACHE_NAME).then(cache => {
return [Link]([Link]).then(cachedResponse => {
const networkFetch = fetch([Link]).then(networkResponse => {
[Link]([Link], [Link]()); // update cache
return networkResponse;
});
return cachedResponse || networkFetch; // serve cache OR wait for network
});
})
);
});

🎯 EXAM FOCUS: Given a resource type, be able to justify which caching strategy is most
appropriate. This appears on every assignment.

15.4 Cache Versioning and Invalidation


"There are only two hard things in Computer Science: cache invalidation and naming things." —
Phil Karlton. Let me show you how PWAs solve cache invalidation.

The strategy is version the cache name, not the individual files. When you deploy a new version
of your app, change CACHE_NAME from 'app-shell-v1' to 'app-shell-v2'. The activate event
deletes all caches not in your whitelist. Old assets are automatically purged, and new assets are
pre-cached. Simple and robust.

For dynamic runtime caches, use LRU (Least Recently Used) eviction with maximum entry
counts to prevent the cache from growing unbounded. Workbox provides this out of the box.
Lecture 16 — Manifest, Installability &
Push Notifications
16.1 The Web App Manifest: Complete Anatomy
The manifest is a JSON file (typically [Link] or [Link]) linked from your HTML:

<link rel="manifest" href="/[Link]">

A complete production manifest:

{
"name": "MIT CourseViewer",
"short_name": "CourseViewer",
"description": "Browse MIT OpenCourseWare offline.",
"start_url": "/?source=pwa",
"scope": "/",
"display": "standalone",
"orientation": "any",
"theme_color": "#A31F34",
"background_color": "#FFFFFF",
"categories": ["education"],
"lang": "en-US",
"icons": [
{ "src": "/icons/[Link]", "sizes": "192x192", "type": "image/png", "purpose":
"any" },
{ "src": "/icons/[Link]", "sizes": "512x512", "type": "image/png", "purpose":
"any" },
{ "src": "/icons/[Link]", "sizes": "512x512", "type": "image/png",
"purpose": "maskable" }
],
"screenshots": [
{ "src": "/screenshots/[Link]", "sizes": "1280x720", "form_factor": "wide" },
{ "src": "/screenshots/[Link]", "sizes": "390x844", "form_factor": "narrow" }
]
}

Field-by-Field Explanation
name vs. short_name
name is used in install prompts and splash screens (can be up to ~30 characters). short_name is
used on the home screen icon label and in tight spaces — keep it under 12 characters or it gets
truncated on Android.

start_url
The URL that opens when the user taps the app icon. Always add a query parameter (?
source=pwa) so your analytics can distinguish users coming through the PWA versus a regular
browser visit.

display
Controls how much browser chrome (UI) is visible. Four possible values:

• fullscreen: No browser UI at all. Used by games. Rarely appropriate for utility apps.
• standalone: Looks like a native app. Has a title bar and system status bar, but no browser
address bar. Most common choice.
• minimal-ui: Has a back button and URL displayed, but nothing else. Good for content
apps.
• browser: Standard browser tab. Effectively no PWA display behavior.

Maskable Icons
Android Adaptive Icons require your icon to have a "safe zone" — the center 80% of the canvas. A
maskable icon has full-bleed artwork that can be cropped into any shape (circle, squircle,
teardrop) by the launcher. Always provide both a standard 512px icon and a maskable 512px icon.

16.2 The Install Prompt


Browsers automatically show an install banner when all installability criteria are met. But auto-
showing it on first visit converts poorly. Best practice: intercept the event, save it, and show a
custom button at a meaningful moment (e.g., after the user has visited 3 times or completed a key
action).

let deferredPrompt;

[Link]('beforeinstallprompt', event => {


[Link](); // stop the automatic banner
deferredPrompt = event; // save it for later
showInstallButton(); // reveal your custom UI
});
[Link]('click', async () => {
[Link](); // show native dialog
const { outcome } = await [Link]; // wait for user decision
[Link]('User chose:', outcome); // 'accepted' or 'dismissed'
deferredPrompt = null; // can only use prompt() once
});

After installation, the appinstalled event fires. Hide your install button and log the conversion
event in your analytics.

16.3 Push Notifications — Architecture


Push notifications involve three parties: your web app (client), your server, and a Push Service
provided by the browser vendor (Google FCM for Chrome, APNs for Safari, Mozilla autopush for
Firefox). Here is the complete flow:

7. User grants permission: The browser shows a permission dialog. You cannot show this
without user gesture on most browsers.
8. Browser subscribes: Your app calls [Link](), which contacts the Push
Service and returns a PushSubscription object containing a unique endpoint URL and
encryption keys.
9. Subscription sent to your server: You POST the subscription object to your backend and
store it in a database.
10. Server sends a message: When you want to notify the user, your server POSTs to the
subscription's endpoint URL with the encrypted payload using the Web Push Protocol (RFC
8030).
11. Push Service delivers: The browser vendor's Push Service delivers the message to the
user's device, waking the service worker if needed.
12. SW shows notification: The SW's push event fires. It calls
[Link]() to display the notification.

// In your main JS — requesting permission and subscribing


const permission = await [Link]();
if (permission !== 'granted') return;

const subscription = await [Link]({


userVisibleOnly: true, // REQUIRED — prevents silent pushes
applicationServerKey: urlB64ToUint8Array(PUBLIC_VAPID_KEY)
});
await fetch('/api/push-subscribe', {
method: 'POST',
body: [Link](subscription)
});

// In [Link] — handling incoming push messages


[Link]('push', event => {
const data = [Link]?.json() ?? {};
[Link](
[Link]([Link], {
body: [Link],
icon: '/icons/[Link]',
badge: '/icons/[Link]',
data: { url: [Link] } // passed to notificationclick
})
);
});

// Handle notification click — open or focus a window


[Link]('notificationclick', event => {
[Link]();
[Link](
[Link]({ type: 'window' }).then(clientList => {
for (const client of clientList) {
if ([Link] === [Link] && 'focus' in client)
return [Link]();
}
return [Link]([Link]);
})
);
});

16.4 VAPID Keys — Why They Exist


VAPID (Voluntary Application Server Identification) is a mechanism that proves to the Push Service
that push messages are really coming from your server, not an attacker who stole your
subscription endpoints. You generate a public/private key pair:

# Generate VAPID keys with web-push library


npx web-push generate-vapid-keys
The public key goes in the client (in [Link]). The private key stays on your server
and signs each push request. The Push Service verifies the signature before delivering. Without
VAPID, any server that obtained a subscription URL could spam your users.

16.5 Background Sync


Background Sync allows deferred actions — operations that should happen "as soon as possible
when the network is available." It is the reliability guarantee for user-generated content (posts,
uploads, form submissions).

// Register a sync in the main page when a user submits a form


async function submitComment(comment) {
await saveToIndexedDB(comment); // save locally first
const reg = await [Link];
await [Link]('sync-comments'); // request background sync
}

// In [Link] — process queued items when sync fires


[Link]('sync', event => {
if ([Link] === 'sync-comments') {
[Link](flushPendingComments());
}
});

async function flushPendingComments() {


const pending = await getFromIndexedDB('pending-comments');
for (const comment of pending) {
await fetch('/api/comments', { method: 'POST', body: [Link](comment) });
await removeFromIndexedDB([Link]);
}
}

📝 LECTURE NOTE: Background Sync has a retry mechanism with exponential backoff. If the
sync fails (e.g., server is down), the browser will retry automatically at increasing intervals.
Lecture 17 — Performance, Workbox &
Deployment
17.1 Performance Metrics: What We Actually Measure
Performance is not just "make it fast." It is a set of precisely defined, user-centric metrics that
correlate with user perception and business outcomes. The Core Web Vitals (Google's
standardized set):

Metric What It Measures Good Threshold Poor Threshold


LCP — Largest Contentful When the main content is
≤ 2.5 sec > 4.0 sec
Paint visible
Lag from first user
FID — First Input Delay interaction to browser ≤ 100 ms > 300 ms
response
CLS — Cumulative Layout How much the page layout
≤ 0.1 > 0.25
Shift jumps during load
Responsiveness of all
INP — Interaction to Next
interactions throughout ≤ 200 ms > 500 ms
Paint
session
TTFB — Time to First Byte Server response time ≤ 800 ms > 1800 ms

17.2 The App Shell Architecture — Engineering the Instant


Load
The App Shell model is a specific architectural pattern for structuring a PWA to achieve instant
perceived load times. The key insight is to separate your application into two layers:

• The Shell (static): The minimal HTML, CSS, and JS needed to render the application's UI
skeleton — header, nav, layout containers, loading spinners. This never changes between
deployments (or changes very rarely). It is pre-cached during SW installation.
• The Content (dynamic): The actual data — posts, search results, user profiles — fetched
from the network on demand. Never pre-cached; always fresh.

The result: on every visit after the first, the shell loads from cache in < 100ms, making the page
feel instant. The content area shows a skeleton UI immediately, then populates as data arrives
from the network. Users perceive this as much faster than a traditional page load, even if total data
transfer time is identical.

17.3 Workbox — Production Service Worker Tooling


Writing service workers from scratch is repetitive and error-prone. Workbox (by Google) is a set of
JavaScript libraries that provide battle-tested, production-ready implementations of caching
strategies, routing, pre-caching, and more. It is the de facto standard for production PWAs.

Workbox Core Concepts


Routing
Workbox provides a router that maps URL patterns to caching strategies:

import { registerRoute } from 'workbox-routing';


import { CacheFirst, NetworkFirst, StaleWhileRevalidate } from 'workbox-strategies';
import { ExpirationPlugin } from 'workbox-expiration';

// Cache First for images


registerRoute(
({ request }) => [Link] === 'image',
new CacheFirst({
cacheName: 'images-cache',
plugins: [new ExpirationPlugin({ maxEntries: 60, maxAgeSeconds: 30 * 24 * 60 * 60 })]
})
);

// Network First for API calls


registerRoute(
({ url }) => [Link]('/api/'),
new NetworkFirst({ cacheName: 'api-cache', networkTimeoutSeconds: 3 })
);

// Stale While Revalidate for fonts


registerRoute(
({ url }) => [Link] === '[Link]
new StaleWhileRevalidate({ cacheName: 'google-fonts' })
);

Precaching with Build Tool Integration


Workbox integrates with build tools (Webpack, Vite, Rollup) via plugins to automatically generate a
precache manifest — a list of all your static assets with their content hashes. The hash is used for
cache invalidation: only assets that actually changed get re-downloaded on the next deploy.

// [Link]
import { VitePWA } from 'vite-plugin-pwa';
export default { plugins: [VitePWA({ registerType: 'autoUpdate' })] };

17.4 Lighthouse — Auditing Your PWA


Lighthouse is both an automated auditing tool and the official specification for what a PWA
must be. Run it from Chrome DevTools > Lighthouse tab, or from the command line:

npx lighthouse [Link] --output html --output-path [Link]

The PWA audit checks the following, among others:

• Is HTTPS enforced with valid certificate?


• Does the manifest have all required fields?
• Are icons provided in the required sizes?
• Does a service worker control the page?
• Does the page work offline? (Lighthouse simulates a network disconnect)
• Is there a valid meta viewport tag?
• Is the page accessible? (ARIA labels, contrast ratios, keyboard navigation)

🎯 EXAM FOCUS: For the final project, your PWA must score ≥ 90 in Performance, ≥ 90 in
Accessibility, and pass all PWA checks in Lighthouse. Scores below these thresholds will require
revision before grading.

17.5 Deployment Checklist


Before shipping a PWA to production, verify every item in this checklist:

Security & Protocol


13. HTTPS enforced — all HTTP requests redirect to HTTPS with HSTS header
14. Content Security Policy (CSP) — prevents XSS attacks
15. VAPID keys rotated from development to production

Manifest & Icons


16. [Link] linked in all HTML pages
17. Both regular and maskable 512px icons present
18. start_url analytics parameter set
19. screenshots added for rich install prompts on Android

Service Worker
20. SW scope covers the entire app
21. All app shell assets pre-cached with correct versioning
22. Offline fallback page for uncached navigations
23. Cache size limits enforced with ExpirationPlugin
24. Error boundaries — if SW fails, app degrades gracefully

Performance
25. LCP < 2.5 seconds on 4G
26. CLS < 0.1 — no layout jumps
27. Images WebP/AVIF format with width/height attributes
28. Third-party scripts deferred or loaded async
29. Critical CSS inlined in <head>

Accessibility
30. All interactive elements keyboard-accessible
31. Color contrast ratio ≥ 4.5:1 for normal text
32. Semantic HTML — correct heading hierarchy, landmark elements
33. All images have descriptive alt text
Advanced Topics & Emerging APIs
File System Access API
Modern PWAs can now read and write files directly to the user's filesystem (with explicit
permission) using the File System Access API. This enables desktop-class apps like Figma, VS
Code Web, and Google Docs to run as PWAs with full file-editing capability. The user grants
access by picking a file or folder through a native OS dialog — no drag-and-drop workarounds
needed.

Web Share & Web Share Target API


The Web Share API allows your PWA to invoke the native OS share sheet (the same one used by
native apps). The Web Share Target API goes further: your PWA can receive shared content from
other apps (images, text, files), registered via your manifest. This closes the last major gap
between web and native for social sharing.

Project Fugu — The Web Capabilities Horizon


Project Fugu is a cross-browser initiative by Google, Microsoft, Samsung, and others to close the
capability gap between web and native. APIs recently shipped or in development include: Web
NFC, Web Bluetooth, Web USB, Web Serial, Idle Detection, Screen Wake Lock, Multi-Screen
Window Placement, Local Font Access, and Eye Dropper. Each new API expands what a PWA
can do without requiring a native wrapper. Track progress at [Link]/fugu-status.
Summary & Exam Study Guide
Concept Map — How Everything Connects
Here is the dependency graph you should have in your head:

• HTTPS → enables Service Workers → enables Caching, Push, Background Sync


• Web App Manifest → enables Installability → enables Add to Home Screen
• Service Worker + Manifest + HTTPS → together satisfy PWA installability criteria →
Lighthouse passes
• Cache Storage + Fetch Event → enable Offline Support
• Push API + Notifications API → together enable Push Notifications
• Background Sync + IndexedDB → together enable Reliable Data Submission

Ten Questions to Test Yourself


34. What are the three mandatory criteria for a browser to consider a web app installable as
a PWA?
35. What is the difference between the install event and the activate event in a service
worker? What should you do in each?
36. A news app wants content to be as fresh as possible but still usable offline. Which
caching strategy should it use for its article list API endpoint, and why?
37. Why must service workers be served over HTTPS? What attack does this requirement
prevent?
38. Explain the 'waiting' phase of the service worker lifecycle. Why does it exist? How do
you bypass it and when should you?
39. What is the role of VAPID keys in push notifications? Who holds the public key and
who holds the private key?
40. Describe the App Shell architecture. What goes in the shell? What goes in the dynamic
layer?
41. What does Workbox's ExpirationPlugin do? Why is it important?
42. What is the beforeinstallprompt event? Why should you capture it rather than letting the
browser show the banner automatically?
43. A service worker's fetch handler runs a network-first strategy. The user goes offline.
Three hours later, a background sync fires and the fetch handler runs again. What
happens?

Recommended Reading
• [Link]/progressive-web-apps — Official Google PWA documentation
• [Link]/en-US/docs/Web/Progressive_web_apps — MDN
comprehensive reference
• [Link]/web/tools/workbox — Workbox documentation
• "High Performance Browser Networking" by Ilya Grigorik — Chapter 15 on HTTP/2
and Chapter 16 on WebRTC are directly relevant
• RFC 8030 — The Web Push Protocol specification. Read at least the abstract and Section
5.

End of Lecture Notes — Lectures 14–17


MIT EECS · 6.S191 · Progressive Web Applications

You might also like