<script>
if (window.addEventListener) {
// 不允许缓存 service-worker.js 因为如果index.html被缓存了,可能导致 service-worker.js不更新
window.addEventListener("load", function() {
// <script src="/js/service-worker.js?t=xxx"><//script>
var script = document.createElement("script")
script.src = `/js/service-worker.js?t=${new Date().getTime()}`
script.async = true;
script.type = "text/javascript";
script.crossOrigin = "anonymous";
document.head.insertBefore(script, document.head.firstChild)
})
}
</script>
// /js/service-worker.js
(function(){
/* 使用动态生成
new webpack.DefinePlugin({
'process.env.cacheStorageKey': `'${new Date().toISOString()}'`
})
*/
const version = new Date().toISOString()
function register () {
if ('serviceWorker' in navigator) {
// 不允许 service-sw.js 被缓存
const url = `/service-sw.js?v=${version}`;
navigator.serviceWorker.register(url).then((registration) => {
var sw = null; var state
if (registration.installing) {
sw = registration.installing
state = 'installing'
} else if (registration.waiting) {
sw = registration.waiting
state = 'installed'
} else if (registration.active) {
sw = registration.active
state = 'activated'
}
state && console.log(`sw state is ${state}`)
if (sw) {
sw.onstatechange = function () {
console.log(`sw state is ${sw.state}`)
}
}
}).catch((err) => {
console.error('sw fail', err)
})
}
}
function unRegister() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready.then(swReg => {
swReg.unregister(result => {
result && console.log("注销 Service Worker 成功")
})
})
}
}
if (fetch) {
fetch("开关接口地址").then(status => {
if (status === 'on') {
register()
} else if (status === 'off') {
unRegister()
}
})
}
})()
/service-sw.js
/*
self: 表示 Service Worker 作用域, 也是全局变量
caches: 表示缓存
skipWaiting: 表示强制当前处在 waiting 状态的脚本进入 activate 状态
clients: 表示 Service Worker 接管的页面
*/
// 缓存静态资源的key
// 当页面发生修改时, 要同时对 cacheStorageKey 进行修改
// 然后重新打开一次页面, 这个时候渲染的页面依然是旧的, 不过可以从 DevTools 看到 此 service work 被安装和激活。
// 之后关闭页面, 再次打开, 就可以见到新的内容了。
const cacheStorageKey = process.env.cacheStorageKey
/*
const ServiceWorkerWebpackPlugin = require('serviceworker-webpack-plugin')
使用 webpack
new ServiceWorkerWebpackPlugin({
// 注意这个项,打包时这个会加上webpack output.path
// 期望生成目录时 /static 目录
filename: '/static/impl-service-sw.js',
entry: path.resolve(__dirname, './static/service-sw.js')
}),
使用 webpack 动态生成 serviceWorkerOption.assets
*/
const cacheList = serviceWorkerOption.assets
// sw.js self === window
self.addEventListener('install', function (e) {
// 缓存静态资源
e.waitUntil(
caches.open(cacheStorageKey)
.then(cache => cache.addAll(cacheList))
.then(() => self.skipWaiting())
)
})
self.addEventListener('activate', function (e) {
console.log('activate callback')
// 更新缓存资源,清除缓存
const promises = caches.keys().then(cacheNames => {
console.log('cacheNames', cacheNames)
return cacheNames.map(name => {
console.log('name', name)
if (name !== cacheStorageKey) {
return caches.delete(name)
}
return Promise.resolve()
})
})
e.waitUntil(
promises.then(list => {
return Promise.all(
list
)
}).then(() => {
return self.clients.claim()
})
)
})
self.addEventListener('fetch', (event) => {
// abandon non-GET requests
if (event.request.method !== 'GET') return
let url = event.request.url
event.respondWith(
caches.open(cacheStorageKey)
.then(cache => {
return cache.match(event.request)
.then(response => {
if (response) {
// return cached file
console.log('cache fetch: ' + url)
return response
}
// make network request
return fetch(event.request)
.then(newreq => {
// console.log('network fetch: ' + url)
if (newreq.ok) cache.put(event.request, newreq.clone())
return newreq
})
// app is offline
.catch(function (error) {
console.error('Fetching failed:', error)
throw error
})
})
})
)
})
self.addEventListener('error', event => {
// 上报错误信息
// 常用的属性:
// event.message
// event.filename
// event.lineno
// event.colno
// event.error.stack
console.log('sw:event.message', event.message, event.filename)
})
self.addEventListener('unhandledrejection', event => {
// 上报错误信息
// 常用的属性:
// event.reason
console.log('sw:event.reason', event.reason)
})