部份內容引自: https://segmentfault.com/a/1190000014311438?utm_source=tag-newestcss
https://www.cnblogs.com/dojo-lzz/p/8047336.htmlhtml
項目代碼webpack
webpack.prod.conf.jsweb
new OfflinePlugin({ responseStrategy: 'network-first', // cache-first // 緩存/網絡優先 safeToUseOptionalCaches: true, // Removes warning for about `additional` section usage caches: { // webpack 打包後須要換的文件 正則匹配 main: [ // 'css/app.*.css', // 'js/vendor.*.js', // 'js/app.*.js', // '/' ], additional: [ ':externals:' // add external assets(from outside of webpack build)
//['**/.*', '**/*.map, '**/*.gz', '**/manifest-last.json'] //
], optional: [ ':rest:' // all unused/uncached assets // '*.js' ] }, // externals: [ // // list assets that are not bundled by webpack here to cache them // '/' // ], externals: null, // 設置外部連接, 例如配置 http://hello.com/getuser, 那麼在請求這個接口的時候就會進行接口緩存
autoUpdate: 1000 * 60 / 20, // 自動更新 // Tell to OfflinePlugin to generate events for ServiceWorker, console.log to some event
ServiceWorker: {
events: true,
navigateFallbackURL: '/static',
publicPath: '/sw.js?t=' + +new Date() // 注意, 一個要加上時間戳
// publicPath: '/sw.js'
},
AppCache: {
FALLBACK: { '/': '/' }
}
// AppCache: false // 不啓用 application cache
})
JS文件 ajax
OfflinePluginRuntime.install({ // 監聽sw事件,當更新ready的時候,調用applyUpdate以跳過等待,新的sw當即接替老的sw onUpdateReady: () => { console.log('SW Event:', 'onUpdateReady') OfflinePluginRuntime.applyUpdate() }, onUpdated: () => { console.log('SW Event:', 'onUpdated') window.swUpdate = true } })
首先介紹一下assets裏面的三個屬性:
main: [] 這裏配置的是serviceWorker在install階段須要緩存的文件清單,若是其中有一個失敗了,那麼整個serviceWorder就會安裝失敗,因此必須謹慎配置json
additional: [] 這裏配置的文件清單會在serviceWorker activate的時候進行緩存,與main不同,若是這裏的文件緩存失敗,不會影響serviceWorker的正常安裝。並且,在接下來頁面的ajax異步請求中,還能進行緩存嘗試segmentfault
optional: [] 這裏配置的文件清單在serviceWorker安裝激活階段不會進行緩存,只有在監聽到網絡請求的時候才進行緩存。瀏覽器
剛纔說到做用域的時候有坑,若是按照上面的文件配置,最後在網頁中會提示,sw最大的做用域權限在/static下面,言外之意這麼寫是沒法將sw的做用域設置在/根路徑下面。
因此這邊須要服務端在返回sw.js的時候手動設置Service-Worker-Allowed頭字段,而且值設置爲/,同時這個文件的緩存時間設爲0,不然,當更新serviceWorker的時候,因爲瀏覽器緩存了sw.js用戶端這邊的serviceWorker沒法第一時間更新。緩存
最後來一張線上項目,在網速極慢的狀況下也能實現秒開網絡
-------------------追加--------------------擴展fetch事件
首先在配置文件裏添加入口
new OfflinePlugin({ responseStrategy: 'network-first', safeToUseOptionalCaches: true, caches: { main: [ // 'css/app.*.css', // 'js/vendor.*.js', // 'js/app.*.js', // '/' ], additional: [ ':externals:' // add external assets(from outside of webpack build) ], optional: [ ':rest:' // all unused/uncached assets // '*.js' ] }, externals: null, autoUpdate: 1000 * 60 / 20, // Tell to OfflinePlugin to generate events for ServiceWorker, console.log to some event ServiceWorker: { events: true, navigateFallbackURL: '/static', entry: path.json(__dirname, './sw-entry.js'), // 重寫sw publicPath: '/sw.js?t=' + +new Date() // publicPath: '/sw.js' }, AppCache: false })
sw.entry.js
self.addEventListener('fetch', function (event) { function cachesMatch (request, cacheName) { return caches.match(request, { cacheName: cacheName }).then(function (response) { return response }) // Return void if error happened (cache not found) ['catch'](function () {}) } function cacheFirst(cacheUrl, CACHE_NAME) { var resource = cachesMatch(cacheUrl, CACHE_NAME).then(function (response) { if (response) { return response; } // Load and cache known assets var fetching = fetch(urlString).then(function (response) { if (!response.ok) { return response; } (function () { var responseClone = response.clone(); var storing = caches.open(CACHE_NAME).then(function (cache) { return cache.put(urlString, responseClone); }).then(function () { console.log('[SW]:', 'Cache asset: ' + urlString); }); event.waitUntil(storing); })(); return response; }); return fetching; }) return resource } function netWorkFirst(cacheUrl, CACHE_NAME) { var resource = fetch(cacheUrl).then(response => { if (response.ok) { var responseClone = response.clone() var storing = caches.open(CACHE_NAME).then(function (cache) { cache.put(cacheUrl, responseClone); }).then(function () { console.log('[SW]:', 'Cache asset: ' + cacheUrl); }); event.waitUntil(storing); return response; } // Throw to reach the code in the catch below throw new Error('Response is not ok'); }) ['catch'](function () { return cachesMatch(cacheUrl, CACHE_NAME); }); return resource } var url = new URL(event.request.url) url.hash = '' var pathname = url.pathname var urlString = url.toString() var cacheUrl = urlString var IS_KANO = /kano\.guahao\.cn/ var IS_STATIC = /\/static\// var IS_HOME = /^\/(e|u|n)\/(\d+)$/ var IS_EDITOR = /^\/editor(?!\.)/ var IS_PREVIEW = /^\/preview(?!\.)/ var CACHE_PREFIX = __wpo.name var CACHE_TAG = __wpo.version var CACHE_NAME = CACHE_PREFIX + ':' + CACHE_TAG var resource = undefined var isGET = event.request.method === 'GET' // 以緩存優先的形式緩存 kano 以及 static/* 靜態資源 if ((cacheUrl.match(IS_KANO) || pathname.match(IS_STATIC)) && isGET) { resource = cacheFirst(cacheUrl, CACHE_NAME) event.respondWith(resource) } // 以網絡優先的形式緩存 editor頁面 preview頁面和 production頁面 if ((pathname.match(IS_HOME) || pathname.match(IS_EDITOR) || pathname.match(IS_PREVIEW)) && isGET) { resource = netWorkFirst(cacheUrl, CACHE_NAME) event.respondWith(resource) } })