使用傳統前端技術加強客戶端緩存能力

本文使用「署名 4.0 國際 (CC BY 4.0)」許可協議,歡迎轉載、或從新修改使用,但須要註明來源。 署名 4.0 國際 (CC BY 4.0)html

本文做者: 蘇洋前端

建立時間: 2018年10月16日 統計字數: 3721字 閱讀時間: 8分鐘閱讀 本文連接: soulteary.com/2018/10/16/…git


使用傳統前端技術加強客戶端緩存能力

前幾天重構以後,Lighthouse 中有一個評分讓我念念不忘:Progressive Web Appgithub

PWA 不算一個新話題了,因此概念性的東西和 API 我就很少作介紹,下面簡單介紹一個無干預更新的緩存方案,總體代碼量在一百行之內,若是你也想在不「大動干戈」的狀況下對站點或者 Web App 進行性能提高的話,能夠了解一下。編程

瀏覽器客戶端代碼

說到 PWA ,咱們能直接想到的,無非是 加強緩存推送能力。而這兩個能力,都是 ServiceWorker API 實現的。(添加桌面圖標這個需求,我不須要,就不介紹了,感興趣能夠自行搜索)瀏覽器

我在以前的重構文章中有簡單聊過訪客數據,其中有一部分訪客使用的客戶端並不支持 Service Worker,因此在使用它的時候,須要使用能力探測的方式引入,好比:緩存

try {
  ('serviceWorker' in navigator) && navigator.serviceWorker.register('/sw.js');
} catch (error) {
  console.error(error);
}
複製代碼

固然,構建壓縮以後,你獲得的結果應該是 drop console 的最簡代碼。不過,若是你不肯定你的運行環境是否有問題,可使用下面帶有調試日誌的版本。bash

try {
  if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('/sw.min.js').then(function(registration) {
      console.log('ServiceWorker registration successful with scope: ', registration.scope);
    }).catch(function(err) {
      console.error('ServiceWorker registration failed: ', err);
    });
  }
} catch (error) {
  console.error(error);
}
複製代碼

ServiceWorker 客戶端代碼

上面介紹瀏覽器客戶端代碼的時候,有引入一個外部腳本依賴 sw.jsdom

在分享代碼以前,有作過 PWA 相關項目或者瞭解的同窗,可能或多或少會在 加強緩存 這個地方被坑到,好比:緩存沒法更新、緩存內容過多沒法寫入。異步

緩存沒法更新有一個簡單有效的解決方案:定時切換緩存使用的 Store。若是再引入當前時間這個因素,能夠保障緩存使用的 Store 不存在資源爭搶的問題。

結合站點內容特色,配合按期清理緩存的腳本,能夠將緩存數量控制在必定的範圍以內。

這裏提供一個小思路,對服務端資源進行二次緩存的時候,能夠設定一個最大緩存時間的策略,而這裏有兩個方案:

  • 對每一個資源設定緩存 TTL
  • 對全部資源設定統一 TTL

我我的選擇第二個方案,犧牲必定的緩存複用,可是有效下降資源之間的版本管理的複雜度。

而須要緩存的資源通常分爲兩類:

  • 短期緩存:頁面或者頁面片斷
  • 相對長時間緩存:圖片等媒體資源,或者有必定跨頁面通用能力的腳本和樣式資源

這裏以10分鐘(調試模式單位替換爲秒)爲一個時間段,爲短期緩存的資源進行緩存。設定星期數爲其餘資源進行緩存週期。

這裏咱們沒必要過度處理 installactive 事件,只須要統一在 fetch 事件中進行緩存更新和清理便可,好比下面這樣:

function weekId() {
  var now = (new Date());
  var dt = new Date(now.getFullYear(), 0, 1);
  return Math.ceil((((now - dt) / 86400000) + dt.getDay() + 1) / 7);
}

var isDevMode = false;
var CACHE_LONG_TTL = 'WEEK_' + weekId();
var CACHE_TINY_TTL = 'TINY_';

function cleanCache(whiteList) {
  return caches.keys().then(function(buckets) {
    return Promise.all(buckets.filter(function(bucket) {
      return whiteList.indexOf(bucket) === -1;
    }).map(function(bucket) {
      return caches.delete(bucket);
    }));
  });
}

self.addEventListener('activate', function(event) {
  event.waitUntil(cleanCache([CACHE_LONG_TTL]));
});

self.addEventListener('fetch', function(event) {

  var url = new URL(event.request.url);
  if (url.protocol.toLowerCase() !== 'https:') return;

  var now = new Date;
  var CACHE_INTERVAL = Math.floor((isDevMode ? now.getSeconds() : now.getMinutes()) / 10);
  var CACHE_KEY = CACHE_TINY_TTL + CACHE_INTERVAL;

  if (url.pathname.endsWith('/') ||
      url.pathname.endsWith('.html') ||
      url.pathname.endsWith('.md') ||
      url.pathname.endsWith('/feed/') ||
      url.pathname.endsWith('/feed') ||
      url.pathname.endsWith('sw.js')) {

    cleanCache([CACHE_LONG_TTL, CACHE_KEY]);

    event.respondWith(caches.open(CACHE_KEY).then(function(cache) {
      return cache.match(url).then(function(response) {
        if (response) return response;
        return fetch(event.request).then(function(response) {
          cache.put(event.request, response.clone());
          return response;
        });
      });
    }));

  } else {
    event.respondWith(caches.open(CACHE_LONG_TTL).then(function(cache) {
      return cache.match(event.request).then(function(response) {
        if (response) return response;
        return fetch(event.request).then(function(response) {
          cache.put(event.request, response.clone());
          return response;
        });
      });
    }));
  }

});
複製代碼

若是你的訪客有很多是 Chrome v50FireFox v46 如下的客戶端,那麼你可能還須要引入下面的 Polyfill : github.com/dominiccoon…

固然,在引入的時候,建議仍是進行壓縮,進一步提升頁面性能(哪怕它是獨立於客戶端腳本另外的異步進程)。

當一切都完成以後,若是順利的話,你的網站的將支持有限時間(本文是10min)的離線訪問,以及重複訪問時更好的響應能力。

另外,你也不須要擔憂 sw.js 被緩存後,這個站點變成「徹底離線」。由於 sw.js 按照規範能夠保證它的最長存在生命週期是 1天,也就是說,將來你的策略更新最多延遲1天。

最後

支持本地緩存後的評分

再次跑分,發現 Lighthouse 已經全面綠色評價了,不過前文中我提過,我不太須要把網站變爲純粹的 PWA 應用,沒有去設定 mainfest ,因此,依舊有 4 點改進建議。

  1. 用戶不可以「安裝」 Web App。
  2. 沒有定義啓動屏幕(仿客戶端體驗)。
  3. 沒有定義頂欄的主題色。
  4. viewport 沒有優化。

這些後面再說吧。

— EOF


我如今有一個小小的折騰羣,裏面彙集了一些喜歡折騰的小夥伴。

在不發廣告的狀況下,咱們在裏面會一塊兒聊聊軟件、HomeLab、編程上的一些問題,也會在羣裏不按期的分享一些技術沙龍的資料。

喜歡折騰的小夥伴歡迎掃碼添加好友。(請註明來源和目的,不然不會經過審覈) 關於折騰羣入羣的那些事

關於折騰羣入羣的那些事

相關文章
相關標籤/搜索