1. <script>
    2. if (window.addEventListener) {
    3. // 不允许缓存 service-worker.js 因为如果index.html被缓存了,可能导致 service-worker.js不更新
    4. window.addEventListener("load", function() {
    5. // <script src="/js/service-worker.js?t=xxx"><//script>
    6. var script = document.createElement("script")
    7. script.src = `/js/service-worker.js?t=${new Date().getTime()}`
    8. script.async = true;
    9. script.type = "text/javascript";
    10. script.crossOrigin = "anonymous";
    11. document.head.insertBefore(script, document.head.firstChild)
    12. })
    13. }
    14. </script>
    1. // /js/service-worker.js
    2. (function(){
    3. /* 使用动态生成
    4. new webpack.DefinePlugin({
    5. 'process.env.cacheStorageKey': `'${new Date().toISOString()}'`
    6. })
    7. */
    8. const version = new Date().toISOString()
    9. function register () {
    10. if ('serviceWorker' in navigator) {
    11. // 不允许 service-sw.js 被缓存
    12. const url = `/service-sw.js?v=${version}`;
    13. navigator.serviceWorker.register(url).then((registration) => {
    14. var sw = null; var state
    15. if (registration.installing) {
    16. sw = registration.installing
    17. state = 'installing'
    18. } else if (registration.waiting) {
    19. sw = registration.waiting
    20. state = 'installed'
    21. } else if (registration.active) {
    22. sw = registration.active
    23. state = 'activated'
    24. }
    25. state && console.log(`sw state is ${state}`)
    26. if (sw) {
    27. sw.onstatechange = function () {
    28. console.log(`sw state is ${sw.state}`)
    29. }
    30. }
    31. }).catch((err) => {
    32. console.error('sw fail', err)
    33. })
    34. }
    35. }
    36. function unRegister() {
    37. if ('serviceWorker' in navigator) {
    38. navigator.serviceWorker.ready.then(swReg => {
    39. swReg.unregister(result => {
    40. result && console.log("注销 Service Worker 成功")
    41. })
    42. })
    43. }
    44. }
    45. if (fetch) {
    46. fetch("开关接口地址").then(status => {
    47. if (status === 'on') {
    48. register()
    49. } else if (status === 'off') {
    50. unRegister()
    51. }
    52. })
    53. }
    54. })()
    1. /service-sw.js
    2. /*
    3. self: 表示 Service Worker 作用域, 也是全局变量
    4. caches: 表示缓存
    5. skipWaiting: 表示强制当前处在 waiting 状态的脚本进入 activate 状态
    6. clients: 表示 Service Worker 接管的页面
    7. */
    8. // 缓存静态资源的key
    9. // 当页面发生修改时, 要同时对 cacheStorageKey 进行修改
    10. // 然后重新打开一次页面, 这个时候渲染的页面依然是旧的, 不过可以从 DevTools 看到 此 service work 被安装和激活。
    11. // 之后关闭页面, 再次打开, 就可以见到新的内容了。
    12. const cacheStorageKey = process.env.cacheStorageKey
    13. /*
    14. const ServiceWorkerWebpackPlugin = require('serviceworker-webpack-plugin')
    15. 使用 webpack
    16. new ServiceWorkerWebpackPlugin({
    17. // 注意这个项,打包时这个会加上webpack output.path
    18. // 期望生成目录时 /static 目录
    19. filename: '/static/impl-service-sw.js',
    20. entry: path.resolve(__dirname, './static/service-sw.js')
    21. }),
    22. 使用 webpack 动态生成 serviceWorkerOption.assets
    23. */
    24. const cacheList = serviceWorkerOption.assets
    25. // sw.js self === window
    26. self.addEventListener('install', function (e) {
    27. // 缓存静态资源
    28. e.waitUntil(
    29. caches.open(cacheStorageKey)
    30. .then(cache => cache.addAll(cacheList))
    31. .then(() => self.skipWaiting())
    32. )
    33. })
    34. self.addEventListener('activate', function (e) {
    35. console.log('activate callback')
    36. // 更新缓存资源,清除缓存
    37. const promises = caches.keys().then(cacheNames => {
    38. console.log('cacheNames', cacheNames)
    39. return cacheNames.map(name => {
    40. console.log('name', name)
    41. if (name !== cacheStorageKey) {
    42. return caches.delete(name)
    43. }
    44. return Promise.resolve()
    45. })
    46. })
    47. e.waitUntil(
    48. promises.then(list => {
    49. return Promise.all(
    50. list
    51. )
    52. }).then(() => {
    53. return self.clients.claim()
    54. })
    55. )
    56. })
    57. self.addEventListener('fetch', (event) => {
    58. // abandon non-GET requests
    59. if (event.request.method !== 'GET') return
    60. let url = event.request.url
    61. event.respondWith(
    62. caches.open(cacheStorageKey)
    63. .then(cache => {
    64. return cache.match(event.request)
    65. .then(response => {
    66. if (response) {
    67. // return cached file
    68. console.log('cache fetch: ' + url)
    69. return response
    70. }
    71. // make network request
    72. return fetch(event.request)
    73. .then(newreq => {
    74. // console.log('network fetch: ' + url)
    75. if (newreq.ok) cache.put(event.request, newreq.clone())
    76. return newreq
    77. })
    78. // app is offline
    79. .catch(function (error) {
    80. console.error('Fetching failed:', error)
    81. throw error
    82. })
    83. })
    84. })
    85. )
    86. })
    87. self.addEventListener('error', event => {
    88. // 上报错误信息
    89. // 常用的属性:
    90. // event.message
    91. // event.filename
    92. // event.lineno
    93. // event.colno
    94. // event.error.stack
    95. console.log('sw:event.message', event.message, event.filename)
    96. })
    97. self.addEventListener('unhandledrejection', event => {
    98. // 上报错误信息
    99. // 常用的属性:
    100. // event.reason
    101. console.log('sw:event.reason', event.reason)
    102. })