#vueconf2017 / Blake Newman / @blakenewman
slides.com/blakenewman
Caching
Offline support
Push Notifications
Code splitting
Accessibility
SSR
Browser prompt to install
to homescreen
Less content to download
Increased engagement
Increased conversions
Version hashes for long-term caching
Bundle size anyalitics
JS chunks pre-loaded or pre-fetched
code-splitting with dynamic import
Service worker for offline caching
$ npm install -g vue-cli
$ vue init pwa my-project
$ cd my-project
$ npm install
$ npm run dev
Server
Service Worker
Cache
Client
WebSocket
Server
Client
Server
Service Worker
Client
WebSocket
Server
Service Worker
Cache
Client
WebSocket
Server
Service Worker
Cache
Client
WebSocket
self.addEventListener('fetch', event => {
const { request } = event;
const url = new URL(request.url);
// Ignore not GET request or is is different origin.
if (request.method !== 'GET' || url.origin !== self.location.origin) return;
event.respondWith(async () => {
try {
// If not online and cache available then use cache
if (!navigator.onLine) {
const cache = await caches.match(request);
if (cache) return cache;
}
// Request message
const response = await self.fetch(request);
// If not 'ok' then do not cache
if (!response.ok) return response;
// Add response to cache
const openCache = await caches.open(CACHE_NAME);
await openCache.put(request, response.clone());
return response;
} catch (error) {
// User is landing on a page.
return request.mode === 'navigate' ? caches.match('./') : '';
}
});
});
Server
Service Worker
Cache
Client
WebSocket
const socketIO = require('socket.io');
const express = require('express');
const app = express();
const io = socketIO(app);
app.post('/api/user', () => {
io.emit('USER_COUNT_UPDATED');
});
app.get('/api/user/count', (req, res) => {
res.json({ count: 99283374 }).end();
});
Server
Service Worker
Cache
Client
WebSocket
import io from 'socket.io-client';
export default store => {
const socket = io.connect();
socket.on('connect', () => store.dispatch('app/connect'));
socket.on('disconnect', () => store.dispatch('app/disconnect'));
socket.on('USER_COUNT_UPDATED', () => store.dispatch('user/getCount'));
};
// Load Socket layer asynchronously in separate chunk
import('./socket').then(socket => socket.default(store));
// Setup service worker
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('service-worker.js', { scope: './' });
}
Server
Service Worker
Cache
Client
WebSocket