perf: download and cache polyfills on-the-fly (#814)

* perf: download and cache polyfills on-the-fly

* fixup the localhost switch for service worker, does nothing
This commit is contained in:
Nolan Lawson 2018-12-15 17:13:46 -08:00 committed by GitHub
parent dbd6c35a88
commit 260f6acf0e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 36 additions and 82 deletions

View file

@ -143,6 +143,7 @@
"ignore": [
"dist",
"src/routes/_utils/asyncModules.js",
"src/routes/_utils/asyncPolyfills.js",
"src/routes/_components/dialog/asyncDialogs.js"
]
},

View file

@ -4,26 +4,6 @@ export const importTimeline = () => import(
/* webpackChunkName: 'Timeline' */ '../_components/timeline/Timeline.html'
).then(getDefault)
export const importIntersectionObserver = () => import(
/* webpackChunkName: 'intersection-observer' */ 'intersection-observer'
)
export const importRequestIdleCallback = () => import(
/* webpackChunkName: 'requestidlecallback' */ 'requestidlecallback'
)
export const importWebAnimationPolyfill = () => import(
/* webpackChunkName: 'web-animations-js' */ 'web-animations-js'
)
export const importIndexedDBGetAllShim = () => import(
/* webpackChunkName: 'indexeddb-getall-shim' */ 'indexeddb-getall-shim'
)
export const importCustomElementsPolyfill = () => import(
/* webpackChunkName: '@webcomponents/custom-elements' */ '@webcomponents/custom-elements'
)
export const importPageLifecycle = () => import(
/* webpackChunkName: 'page-lifecycle' */ 'page-lifecycle/dist/lifecycle.mjs'
).then(getDefault)

View file

@ -0,0 +1,19 @@
export const importIntersectionObserver = () => import(
/* webpackChunkName: '$polyfill$-intersection-observer' */ 'intersection-observer'
)
export const importRequestIdleCallback = () => import(
/* webpackChunkName: '$polyfill$-requestidlecallback' */ 'requestidlecallback'
)
export const importWebAnimationPolyfill = () => import(
/* webpackChunkName: '$polyfill$-web-animations-js' */ 'web-animations-js'
)
export const importIndexedDBGetAllShim = () => import(
/* webpackChunkName: '$polyfill$-indexeddb-getall-shim' */ 'indexeddb-getall-shim'
)
export const importCustomElementsPolyfill = () => import(
/* webpackChunkName: '$polyfill$-@webcomponents/custom-elements' */ '@webcomponents/custom-elements'
)

View file

@ -4,7 +4,7 @@ import {
importIntersectionObserver,
importRequestIdleCallback,
importWebAnimationPolyfill
} from './asyncModules'
} from './asyncPolyfills'
export function loadPolyfills () {
return Promise.all([

View file

@ -10,7 +10,7 @@ function onUpdateFound (registration) {
})
}
if (!location.origin.match('localhost') && 'serviceWorker' in navigator) {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/service-worker.js').then(registration => {
registration.addEventListener('updatefound', () => onUpdateFound(registration))
})

View file

@ -4,6 +4,11 @@ import {
routes as __routes__
} from '../__sapper__/service-worker.js'
import {
get,
post
} from './routes/_utils/ajax'
const timestamp = process.env.SAPPER_TIMESTAMP
const ASSETS = `assets_${timestamp}`
const WEBPACK_ASSETS = `webpack_assets_${timestamp}`
@ -17,7 +22,8 @@ const assets = __assets__
// `shell` is an array of all the files generated by webpack
// also contains '/index.html' for some reason
const webpackAssets = __shell__
.filter(filename => !filename.endsWith('.map'))
.filter(filename => !filename.endsWith('.map')) // don't bother with sourcemaps
.filter(filename => !filename.includes('$polyfill$')) // polyfills are cached on-demand
// `routes` is an array of `{ pattern: RegExp }` objects that
// match the pages in your src
@ -93,6 +99,13 @@ self.addEventListener('fetch', event => {
return response
}
}
// for polyfills, cache them on-the-fly
if (url.pathname.includes('$polyfill$')) {
let response = await fetch(req)
// cache asynchronously, don't wait
caches.open(WEBPACK_ASSETS).then(cache => cache.put(req, response))
return response.clone()
}
}
// for everything else, go network-only
@ -247,62 +260,3 @@ self.addEventListener('notificationclick', event => {
}
})())
})
// Copy-paste from ajax.js
async function get (url, headers, options) {
return _fetch(url, makeFetchOptions('GET', headers), options)
}
async function post (url, body, headers, options) {
return _putOrPostOrPatch('POST', url, body, headers, options)
}
async function _putOrPostOrPatch (method, url, body, headers, options) {
let fetchOptions = makeFetchOptions(method, headers)
if (body) {
if (body instanceof FormData) {
fetchOptions.body = body
} else {
fetchOptions.body = JSON.stringify(body)
fetchOptions.headers['Content-Type'] = 'application/json'
}
}
return _fetch(url, fetchOptions, options)
}
async function _fetch (url, fetchOptions, options) {
let response
if (options && options.timeout) {
response = await fetchWithTimeout(url, fetchOptions, options.timeout)
} else {
response = await fetch(url, fetchOptions)
}
return throwErrorIfInvalidResponse(response)
}
async function throwErrorIfInvalidResponse (response) {
let json = await response.json()
if (response.status >= 200 && response.status < 300) {
return json
}
if (json && json.error) {
throw new Error(response.status + ': ' + json.error)
}
throw new Error('Request failed: ' + response.status)
}
function fetchWithTimeout (url, fetchOptions, timeout) {
return new Promise((resolve, reject) => {
fetch(url, fetchOptions).then(resolve, reject)
setTimeout(() => reject(new Error(`Timed out after ${timeout / 1000} seconds`)), timeout)
})
}
function makeFetchOptions (method, headers) {
return {
method,
headers: Object.assign(headers || {}, {
'Accept': 'application/json'
})
}
}