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