Making GitLab a PWA
Or, how I learned to stop worrying and embrace the boring solution.
Sam Beckham
@samdbeckham
3G
WiFi
1s
3s
3G
WiFi
0.3s
0.3s
Offline
3G
WiFi
0.3s
0.3s
0.3s
It's a
website
that acts like a
native app
Install to
your device
Load offline
It's a
progressive enhancement
Just a
website
The
boring
solution
Lighthouse
✅ Non-Javascript content
✅ https
✅ https redirect
✅ Fast enough on 3g
✅ <meta viewport> tag
✅ Sized for the viewport
❌ Custom splash screen
❌ Themed Address Bar
❌ Service Worker
❌ 200 When offline
❌ Prompted to install the web app
Lighthouse
❌
Custom Splash Screen
❌
Themed Address bar
{
"name": "GitLab",
"short_name": "GitLab",
"display": "standalone",
"scope": "/",
"start_url": "/",
"theme_color": "#474D57",
"background_color": "#ffffff",
"icons": [
{
"src": "touch-icon-ipad-retina.png",
"sizes": "152x152",
"type": "image/png"
}
]
}
./manifest.json
<head>
<!-- other head stuff -->
<link rel="manifest" href="/manifest.json">
</head>
./yourpage.html
Lighthouse
✅ Custom splash screen
✅ Themed Address Bar
❌ Service Worker
❌ 200 When offline
❌ Prompted to install the web app
❌
Service worker
💻
🌐
Browser
Server
request
response
💻
🌐
Browser
Server
🐲
Service
Worker
💻
🌐
Browser
Server
🐲
Service
Worker
cat.png
cat.png
💵
Cache
💻
🌐
Browser
Server
🐲
Service
Worker
cat.png
cat.png
💵
Cache
💤
❌
200 when offline
The
boring
solution
❌
200 when offline
The
boring
solution
💻
🌐
Browser
Server
🐲
Service
Worker
offline.html
💵
Cache
💻
🌐
Browser
Server
🐲
Service
Worker
issue
offline
💵
Cache
issue
❌
💧
// Register a service worker if our
// browser allows it
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register(
'/service_worker.js',
{ scope: '/'}
);
}
Register
const CURRENT_CACHE = '...'
self.addEventListener('install', event => {
event.waitUntil(
caches
.open(CURRENT_CACHE)
.then(cache =>
cache.add('/offline'))
)
})
Install
./service_worker.js
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cache =>
cache !== CURRENT_CACHE
? caches.delete(cache)
: Promise.resolve()
),
...
})
Activate
./service_worker.js
self.addEventListener('fetch', ({request}) => {
event.respondWith(
fetch(request).catch(() => (
caches.match('/offline')
))
)
})
Fetch
./service_worker.js
const CURRENT_CACHE = '<%= Gitlab.version %>_<%= Gitlab.revision %>';
// eslint-disable-next-line no-restricted-globals
self.addEventListener('install', event => {
event.waitUntil(caches.open(CURRENT_CACHE).then(cache => cache.add('/offline')));
});
// eslint-disable-next-line no-restricted-globals
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cache =>
cache !== CURRENT_CACHE ? caches.delete(cache) : Promise.resolve(),
),
);
}),
);
});
// eslint-disable-next-line no-restricted-globals
self.addEventListener('fetch', { request } => {
event.respondWith(
fetch(request).catch(() => (
request.mode === 'navigate' ? caches.match('/offline') : null
)),
);
});
./service_worker.js
Lighthouse
❌ Prompted to install the web app
✅ Service worker
✅ 200 when offline
✅ https
✅ http => https
✅ Custom Splash Screen
✅ Themed Address bar
✅ non-javascript content
✅ Fast enough on 3g
✅ <meta viewport> tag
✅ Sized correctly for the viewport
❌
Prompted to install the web app
Do literally nothing else
✅ Service worker
Lighthouse
✅ 200 when offline
✅ https
✅ http => https
✅ Prompted to install the web app
✅ Custom Splash Screen
✅ Themed Address bar
✅ non-javascript content
✅ Fast enough on 3g
✅ <meta viewport> tag
✅ Sized correctly for the viewport
Gotchas
CI⚡️CD
Naming
clashes
/offline
/-/offline
Request
types
self.addEventListener('fetch', ({request}) => {
if (
request.method === 'GET' &&
request.mode === 'navigate'
) {
event.respondWith(
fetch(request).catch(() => (
caches.match('/-/offline')
))
)
}})
Fetch
./service_worker.js
CI⚡️CD
CI⚡️CD
✅ Basic PWA Support
✅ Vulnerability Caching
June 22nd 2019
v12.0
Thanks!
Sam Beckham
@samdbeckham
Making GitLab a PWA
By Sam Beckham
Making GitLab a PWA
- 1,234