PWA做離線數(shù)據(jù)緩存實現(xiàn)與探索

PWA

Service workerPWA得以實現(xiàn)的核心技術(shù)

service worker

Service worker 是一個獨立的worker線程廷雅,獨立于當前網(wǎng)頁進程,是一種特殊的web worker京髓。主要功能在生命周期函數(shù)中實現(xiàn)航缀。

service worker注冊

if('serviceWorker' in navigator) {
  const sw = await navigator.serviceWorker.register(serviceWorker文件路徑);
}
//使用serviceworker-webpack-plugin插件注冊方式
import runtime from 'serviceworker-webpack-plugin/lib/runtime'
if('serviceWorker' in navigator) {
  const sw = await runtime.register();
}

serviceworker-webpack-plugin插件可以將所有打包后的目錄文件注入到打包后的sw.js文件,通過global.serviceWorkerOptions.assets獲取所有目錄文件名堰怨,便于做靜態(tài)資源的緩存芥玉。

PWA用到的service worker生命周期函數(shù)

  • install緩存所有你需要的靜態(tài)資源

    self.addEventListener('install', async () => {
      console.log('service worker installing');
      const cache = await caches.open(CACHE_NAME); //cacheStorage中緩存的名稱
      const CACHE_URL = global.serviceWorkerOptions.assets.concat(['/']); //緩存的靜態(tài)資源目錄,不要忘記緩存'/'目錄文件备图,在斷網(wǎng)情況下飞傀,頁面首先加載的是'/'目錄資源皇型。
      await cache.addAll(CACHE_URL); //此處只要有一個資源無法下載,靜態(tài)資源的緩存都會失敗
      await self.skipWaiting(); //跳過等待砸烦,保持運行最新的service worker
    })
    
  • active刪除舊的緩存

    self.addEventListener('activate', async () => {
      console.log('service worker activate');
      const cacheKeys = await caches.keys();
      cacheKeys.map(async item => { //刪除舊的緩存
        if (item !== CACHE_NAME) {
          await caches.delete(item);
        }
      })
      await self.clients.claim();//接管所有頁面
    }
    
  • fetch可以攔截所有的請求,并做數(shù)據(jù)緩存

    self.addEventListener('fetch', async e => {
      console.log('service worker fetch');
      const req = e.request;
      const url = new URL(req.url);
      const api = new URL(apiHost); //自己使用的請求域名
      let isNetworkerFirst, isCacheFirst;
      if (isNetworkFirst) {
        e.respondWith(networkFirst(req)); //網(wǎng)絡(luò)優(yōu)先
      } else if (isCahceFirst) {
        e.respondWith(cacheFirst(req));//緩存優(yōu)先
      }
    })
    
    const cacheFirst = async req => { //先從緩存中獲取數(shù)據(jù)绞吁,如果沒有匹配到幢痘,再發(fā)起網(wǎng)絡(luò)請求
      const cache = await caches.open(CACHE_NAME);
      let cacheData = await cache.match(req);
      if (!cacheData) {
        cacheData = await fetch(req);
        if (!cacheData || cacheData.status !== 200) return cacheData;
        const cache = await caches.open(CACHE_NAME);
        cache.put(req, cacheData.clone());
      }
      return cacheData;
    };
    
    const networkFirst = async req => { //先發(fā)起網(wǎng)絡(luò)請求,如果失敗則再從緩存中匹配
      const cache = await caches.open(CACHE_NAME);
      let fetchResult;
      try {
        await Promise.race([requestPromise(req).then(res => {
          fetchResult = res;
          if (timer) clearTimeout(timer);
          if (isNetworkSlowly) isNetworkSlowly = false;
          hasShowNotification = false;
        }), timeout_promise()]);
        if (!fetchResult || fetchResult.status !== 200) return fetchResult;
        cache.put(req, fetchResult.clone());
        return fetchResult;
      } catch (e) {
        const cacheData = await cache.match(req);
        if (navigator.onLine && cacheData && e === 'request timeout' && isNetworkSlowly && !hasShowNotification) {
          showLocalNotification('網(wǎng)絡(luò)不給力家破,當前訪問的是緩存數(shù)據(jù)');
          isNetworkSlowly = false;
          hasShowNotification = true;
        }
        console.log(e, cacheData, 'error')
        return cacheData;
      }
    }
    
    //設(shè)置一定時間颜说,在原本的fetch請求還沒有響應(yīng)的情況下,讓service worker中的fetch報出’request timeout‘錯誤汰聋,從而轉(zhuǎn)向向cache中匹配請求資源门粪,實現(xiàn)在弱網(wǎng)情況下的網(wǎng)頁正常瀏覽
    const timeout_promise = () => {
      return new Promise((resolve, reject) => {
        timer = setTimeout(() => {
          if (!isNetworkSlowly) isNetworkSlowly = true;
          reject('request timeout');
        }, 9000);
      });
    } 
    
    const showLocalNotification = (title, body) => {
      const options = {};
      try {
        self.registration.showNotification(title, options);
      } catch (error) {
        console.warn(error);
      }
    };
    

    根據(jù)自己的需求選擇網(wǎng)絡(luò)優(yōu)先還是緩存優(yōu)先,例如isNetworkFirst = url.origin === self.origin && req.method === 'GET'烹困,e.respondWith()對攔截的請求玄妈,把緩存匹配的或者網(wǎng)絡(luò)請求到的數(shù)據(jù)返回,作出最后的響應(yīng)髓梅。self.registration.showNotification()向瀏覽器發(fā)送消息拟蜻。

其他相關(guān)

  • 緩存使用到的cacheStorage可見詳情文檔

  • 瀏覽器可以通過addEventListener('offline', () => {})addEventListener('online', () => {})來監(jiān)聽瀏覽器網(wǎng)絡(luò)在線與離線狀態(tài)。但無法判斷弱網(wǎng)狀態(tài)枯饿,弱網(wǎng)狀態(tài)請求緩存數(shù)據(jù)的具體實現(xiàn)也可根據(jù)自己的項目邏輯來定(例如:請求超過10秒還未得到響應(yīng)酝锅,判定為弱網(wǎng)狀態(tài))。

  • service worker 注冊后奢方,對靜態(tài)資源的下載緩存會占用部分帶寬搔扁,影響項目首頁的加載速度,可以設(shè)置一個定時器蟋字,在一定的時間后才啟動注冊程序稿蹲。

  • cacheStorage無法緩存POST請求的數(shù)據(jù)。

  • 向瀏覽器發(fā)送消息愉老,首先需要獲取相應(yīng)的權(quán)限场绿。permission = await window.Notification.requestPermission()獲取瀏覽器發(fā)送提醒消息權(quán)限,permission = 'granted'時嫉入,允許發(fā)送消息焰盗。

  • 使用self.skipWaiting()可以保證執(zhí)行最新的sw,但新舊sw的交替咒林,往往都要經(jīng)過service workerinstall->waiting->active熬拒,因此總會有頁面前后期由不同的sw來處理的問題。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末垫竞,一起剝皮案震驚了整個濱河市澎粟,隨后出現(xiàn)的幾起案子蛀序,更是在濱河造成了極大的恐慌,老刑警劉巖活烙,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件徐裸,死亡現(xiàn)場離奇詭異,居然都是意外死亡啸盏,警方通過查閱死者的電腦和手機重贺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來回懦,“玉大人气笙,你說我怎么就攤上這事∏釉危” “怎么了潜圃?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長舟茶。 經(jīng)常有香客問我谭期,道長,這世上最難降的妖魔是什么稚晚? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任崇堵,我火速辦了婚禮,結(jié)果婚禮上客燕,老公的妹妹穿的比我還像新娘鸳劳。我一直安慰自己,他們只是感情好也搓,可當我...
    茶點故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布赏廓。 她就那樣靜靜地躺著,像睡著了一般傍妒。 火紅的嫁衣襯著肌膚如雪幔摸。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天颤练,我揣著相機與錄音既忆,去河邊找鬼。 笑死嗦玖,一個胖子當著我的面吹牛患雇,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播宇挫,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼苛吱,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了器瘪?” 一聲冷哼從身側(cè)響起翠储,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤绘雁,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后援所,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體庐舟,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年任斋,在試婚紗的時候發(fā)現(xiàn)自己被綠了继阻。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡废酷,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出抹缕,到底是詐尸還是另有隱情澈蟆,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布卓研,位于F島的核電站趴俘,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏奏赘。R本人自食惡果不足惜寥闪,卻給世界環(huán)境...
    茶點故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望磨淌。 院中可真熱鬧疲憋,春花似錦、人聲如沸梁只。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽搪锣。三九已至秋忙,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間构舟,已是汗流浹背灰追。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留狗超,地道東北人弹澎。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像抡谐,于是被迫代替她去往敵國和親裁奇。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,512評論 2 359