摘要:導(dǎo)入谷歌提供的庫注冊成功可以進(jìn)行下一步的操作立即激活跳過等待下面用官網(wǎng)給出的幾張圖解釋一下所提供的幾種緩存策略而它們正好能滿足上文我們自己用代碼所實現(xiàn)的效果。接下來讓我們使用去實現(xiàn)上文優(yōu)化前端性能的緩存策略。
前言 :
說起前端性能優(yōu)化, 我們首先想到的可能就是用 Gulp 、Webpack 之類的自動化構(gòu)建工具對 HTML、CSS 、JS 代碼進(jìn)行壓縮,同時優(yōu)化圖片資源。再者就是使用 CSS Sprite 或者對于較小的圖片用 base64 直接編碼來進(jìn)行優(yōu)化。當(dāng)然還有很多可以優(yōu)化的方向, 例如考慮瀏覽器緩存、頁面渲染性能 ( 減少重排與重繪和 GPU 硬件加速 ) 、JS阻塞性能等等。但我們今天講的是如何利用緩存策略在適宜的情況下直接減少對前端數(shù)據(jù)的請求量從而達(dá)到前端性能的優(yōu)化。因此 Service Worker 以及其相關(guān)的 API 就成為了我們今天的主角。
提醒 : 本篇文章將直接講述如何利用 Service Worker 對前端性能進(jìn)行優(yōu)化, 希望讀者在此之前已經(jīng)對 Service Worker 有基本的了解, 若之前沒有接觸過, 可以先看看以下的兩篇文章。
Service Worker ~ Google ( 墻 )
Service Worker 簡介
制定緩存策略首先, 既然是前端性能優(yōu)化, 我們就需要想想該如何制定緩存策略才能達(dá)到理想的效果。我們可能有這樣的想法, 即對 CSS 、JS 等易更改文件優(yōu)先使用網(wǎng)絡(luò)請求的數(shù)據(jù), 而對于圖片資源則優(yōu)先使用緩存。如果再進(jìn)一步思考的話, 我們也許會希望在網(wǎng)絡(luò)條件好的情況下優(yōu)先使用網(wǎng)絡(luò)請求數(shù)據(jù), 而網(wǎng)絡(luò)條件較差時則盡可能的直接使用緩存。嗯 ~ 看起來還不錯, 那么根據(jù)以上的兩點我們先用代碼來實現(xiàn)一下吧。
先邁出最簡單的第一步, 注冊 Service Worker。
// index.js if ( "serviceWorker" in navigator ) { navigator.serviceWorker.register("/sw.js") .then( registration => { console.log("ServiceWorker registration successful with scope: ", registration.scope); }) .catch( err => console.log("ServiceWorker registration failed: ", err)); }
在 sw.js 中實現(xiàn)常規(guī)操作。
// sw.js var cacheMaps = { cache_file: "css.js", cache_image: "images" } self.addEventListener("install", () => { // 一般注冊以后,激活需要等到再次刷新頁面后再激活 // 可防止出現(xiàn)等待的情況,這意味著服務(wù)工作線程在安裝完后立即激活 self.skipWaiting(); }) // 運行觸發(fā)的事件 self.addEventListener("activate", event => { event.waitUntil( // 若緩存數(shù)據(jù)更改,則在這里更新緩存 caches.keys() .then( cacheNames => { return cacheNames.filter( item => !Object.values(cacheMaps).includes(item)) }) .then( keys => { return Promise.all( keys.map( key => { return caches.delete(key); })) }) // 更新客戶端上的 Service Worker 腳本 .then(() => self.clients.claim()) ) })
實現(xiàn)網(wǎng)絡(luò)優(yōu)先的邏輯。
function firstNet(cacheName, request) { // 請求網(wǎng)絡(luò)數(shù)據(jù)并緩存 return fetch(request).then( response => { var responseCopy = response.clone(); caches.open(cacheName).then( cache => { cache.put(request, responseCopy); }); return response; }).catch(() => { return caches.open(cacheName).then( cache => { return cache.match(request); }); }); }
實現(xiàn)緩存優(yōu)先的邏輯。
function firstCache(cacheName, request) { return caches.open(cacheName).then( cache => { return cache.match(request).then( response => { var fetchServer = function() { return fetch(request).then( newResponse => { cache.put(request, newResponse.clone()); return newResponse; }); } // 如果緩存中有數(shù)據(jù)則返回,否則請求網(wǎng)絡(luò)數(shù)據(jù) if (response) { return response; } else { return fetchServer(); } }); }); }
完成緩存策略中我們提到的第一點,即對 CSS 、JS 請求使用網(wǎng)絡(luò)優(yōu)先,圖片資源請求實現(xiàn)緩存優(yōu)先。
// sw.js self.addEventListener("fetch", event => { var request = event.request, url = request.url, cacheName; // 網(wǎng)絡(luò)優(yōu)先 if ( /.(js|css)$/.test(url) ) { (cacheName = cacheMaps.cache_file) && e.respondWith(firstNet(cacheName, request)); } // 緩存優(yōu)先 else if ( /.(png|jpg|jpeg|gif|webp)$/.test(url) ) { (cacheName = cacheMaps.cache_image) && e.respondWith(firstCache(cacheName, request)); } })
接下來我們利用 Promise.race() 完成一個競速模式, 從而實現(xiàn)上文提到的第二點即根據(jù)網(wǎng)絡(luò)條件的好壞執(zhí)行相應(yīng)的操作。
function networkCacheRace(cacheName, request) { var timer, TIMEOUT = 500; /** * 網(wǎng)絡(luò)好的情況下給網(wǎng)絡(luò)請求500ms, 若超時則從緩存中取數(shù)據(jù) * 若網(wǎng)絡(luò)較差且沒有緩存, 由于第一個Promise會一直處于pending, 故此時等待網(wǎng)絡(luò)請求響應(yīng) */ return Promise.race([new Promise((resolve, reject) => { timer = setTimeout(() => { caches.open(cacheName).then( cache => { cache.match(request).then( response => { if (response) { resolve(response); } }); }); }, TIMEOUT); }), fetch(request).then( response => { clearTimeout(timer); var responseCopy = response.clone(); caches.open(cacheName).then( cache => { cache.put(request, responseCopy); }); return response; }).catch(() => { clearTimeout(timer); return caches.open(cacheName).then( cache => { return cache.match(request); }); })]); }
現(xiàn)在我們可以在 sw.js 中更改一下緩存策略,從而達(dá)到最理想的效果。
// sw.js self.addEventListener("fetch", event => { // ... if ( /.(js|css)$/.test(url) ) { (cacheName = cacheMaps.cache_file) && e.respondWith(networkCacheRace(cacheName, request)); } // ... })更好的方案 : Workbox
什么是 Workbox ? 我們可以看看谷歌開發(fā)者官網(wǎng)中給出的解釋。
Workbox is a library that bakes in a set of best practices and removes the boilerplate every developer writes when working with service workers.
其大概意思是它對常見的 Service Worker 操作進(jìn)行了一層封裝, 根據(jù)最佳實踐方便了開發(fā)者的使用。因此在我們快速開發(fā)自己的 PWA 應(yīng)用時使用 Workbox 是最合適不過的了。
它主要有以下幾大功能 :
Precaching ~ 預(yù)緩存
Runtime caching ~ 運行時緩存
Strategies ~ 緩存策略
Request routing ~ 請求路由控制
Background sync ~ 后臺同步
etc ...
基于本文的內(nèi)容, 在這里我們只談?wù)勅绾魏唵蔚氖褂?Workbox 以及它所提供的幾種緩存策略。
注意在 index.js 里面的注冊操作不會改變, 變化的是 sw.js 中的代碼。
// sw.js // 導(dǎo)入谷歌提供的 Workbox 庫 importScripts("https://storage.googleapis.com/workbox-cdn/releases/3.2.0/workbox-sw.js"); if ( !workbox ) { console.log(`Workbox didn"t load.`); return; } // Workbox 注冊成功, 可以進(jìn)行下一步的操作 // 立即激活, 跳過等待 workbox.skipWaiting(); workbox.clientsClaim(); // workbox.routing.registerRoute()...
下面用官網(wǎng)給出的幾張圖解釋一下 Workbox 所提供的幾種緩存策略, 而它們正好能滿足上文我們自己用代碼所實現(xiàn)的效果。
Stale-While-Revalidate Cache First Network First Cache Only Network Only接下來讓我們使用 Workbox 去實現(xiàn)上文優(yōu)化前端性能的緩存策略。
緩存優(yōu)先 :
workbox.routing.registerRoute( /.(png|jpg|jpeg|gif|webp)$/, // 對于圖片資源使用緩存優(yōu)先 workbox.strategies.cacheFirst({ cacheName: "images", // 設(shè)置最大緩存數(shù)量以及過期時間 plugins: [ new workbox.expiration.Plugin({ maxEntries: 60, maxAgeSeconds: 7 * 24 * 60 * 60, }), ], }), );
網(wǎng)絡(luò)優(yōu)先 :
workbox.routing.registerRoute( /.(js|css)$/, workbox.strategies.staleWhileRevalidate({ cacheName: "css.js", }), );
由上文圖中可看出 stale-while-revalidate 策略與我們實現(xiàn)的網(wǎng)絡(luò)優(yōu)先稍有不同, 確切的來說更加明智, 因為除了第一次需要網(wǎng)絡(luò)請求, 接下來的請求會直接從緩存中取數(shù)據(jù)但在頁面加載之后會立即更新緩存, 這樣既保證了加載速度又能每次將數(shù)據(jù)準(zhǔn)確的更新到最新版本。
競速模式 :
workbox.routing.registerRoute( /.(js|css)$/, workbox.strategies.networkFirst({ // 給網(wǎng)絡(luò)請求0.5秒,若仍未返回則從緩存中取數(shù)據(jù) networkTimetoutSeconds: 0.5, cacheName: "css.js", }), );
回頭看看我們手動實現(xiàn)的緩存策略, 顯然使用 Workbox 要簡單的多。當(dāng)然 Workbox 中還有很多東西需要注意, 但由于已經(jīng)超出了文章所講的主要內(nèi)容因此在這里無法具體闡述, 建議讀者還是到官網(wǎng)去仔細(xì)看看文檔詳細(xì)了解一下,若因為墻的問題可以看看第二篇文章。
Workbox ~ Google ( 墻 )
神奇的 Workbox 3.0
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/95603.html
摘要:安裝后已經(jīng)完成了安裝,并且等待其他的線程被關(guān)閉。激活后在這個狀態(tài)會處理事件回調(diào)提供了更新緩存策略的機(jī)會。并可以處理功能性的事件請求后臺同步推送。廢棄狀態(tài)這個狀態(tài)表示一個的生命周期結(jié)束。 showImg(https://segmentfault.com/img/bVbwWJu?w=2056&h=1536); 不知不覺,已經(jīng)來到了最后的下篇 其實我寫的東西你如果認(rèn)真去看,跟著去寫,應(yīng)該能有...
摘要:前端每周清單專注前端領(lǐng)域內(nèi)容,以對外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點分為新聞熱點開發(fā)教程工程實踐深度閱讀開源項目巔峰人生等欄目。 前端每周清單專注前端領(lǐng)域內(nèi)容,以對外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點;分為新聞熱點、開發(fā)教程、工程實踐、深度閱讀、開源項目、巔峰人生等欄目。歡迎關(guān)注【前端之巔】微信公眾號(ID:frontshow),及時獲取前端每周清單;本文則是對于...
摘要:雖然有著各種各樣的不同,但是相同的是,他們前端優(yōu)化不完全指南前端掘金篇幅可能有點長,我想先聊一聊閱讀的方式,我希望你閱讀的時候,能夠把我當(dāng)作你的競爭對手,你的夢想是超越我。 如何提升頁面渲染效率 - 前端 - 掘金Web頁面的性能 我們每天都會瀏覽很多的Web頁面,使用很多基于Web的應(yīng)用。這些站點看起來既不一樣,用途也都各有不同,有在線視頻,Social Media,新聞,郵件客戶端...
摘要:往往純的單頁面應(yīng)用一般不會太復(fù)雜,所以這里不引入和等等,在后面復(fù)雜的跨平臺應(yīng)用中我會將那些技術(shù)一擁而上。構(gòu)建極度復(fù)雜,超大數(shù)據(jù)的應(yīng)用。 showImg(https://segmentfault.com/img/bVbvphv?w=1328&h=768); React為了大型應(yīng)用而生,Electron和React-native賦予了它構(gòu)建移動端跨平臺App和桌面應(yīng)用的能力,Taro則賦...
摘要:往往純的單頁面應(yīng)用一般不會太復(fù)雜,所以這里不引入和等等,在后面復(fù)雜的跨平臺應(yīng)用中我會將那些技術(shù)一擁而上。構(gòu)建極度復(fù)雜,超大數(shù)據(jù)的應(yīng)用。 showImg(https://segmentfault.com/img/bVbvphv?w=1328&h=768); React為了大型應(yīng)用而生,Electron和React-native賦予了它構(gòu)建移動端跨平臺App和桌面應(yīng)用的能力,Taro則賦...
閱讀 2382·2021-11-08 13:13
閱讀 1326·2021-10-09 09:41
閱讀 1771·2021-09-02 15:40
閱讀 3258·2021-08-17 10:13
閱讀 2604·2019-08-29 16:33
閱讀 3203·2019-08-29 13:17
閱讀 3192·2019-08-29 11:00
閱讀 3350·2019-08-26 13:40