用Service Worker實現離線應用

開始有興趣瞭解Service Worker,是由於學習react時使用create-react-app建立的應用,src下面會有一個registerServiceWorker.js文件。後來在瞭解PWA時也看到了它的身影。因而就打算寫一篇筆記詳細瞭解一下。javascript

什麼是Service Worker

Service workers essentially act as proxy servers that sit between web applications, and the browser and network (when available). They are intended to (amongst other things) enable the creation of effective offline experiences, intercepting network requests and taking appropriate action based on whether the network is available and updated assets reside on the server. They will also allow access to push notifications and background sync APIs.

— from MDNcss

翻譯過來就是:Service workers 本質上充當Web應用程序與瀏覽器之間的代理服務器,也能夠在網絡可用時做爲瀏覽器和網絡間的代理。它們旨在(除其餘以外)使得可以建立有效的離線體驗攔截網絡請求並基於網絡是否可用以及更新的資源是否駐留在服務器上來採起適當的動做。他們還容許訪問推送通知和後臺同步API。java

service worker運行在非主線程的其餘線程上,因此不會阻塞主線。,有本身獨立的上下文,不能訪問DOM。只能使用異步api。而且爲了安全,service worker只能運行在https之上。部分瀏覽器的隱私模式也沒法使用。react

因爲service workers是由chrome提出推廣的技術,因此chrome支持最好。其餘瀏覽器的支持狀況就參考Can I Use了:git

clipboard.png

生命週期

盜用MDN的一張圖。github

clipboard.png

離線應用相關接口

CacheStorage 在瀏覽器上的引用名叫 caches,CacheStorage 是多個 Cache 的集合,而每一個 Cache 能夠存儲多個 Response 對象。雖然它是被定義在 ServiceWorker 的規範中,但能夠在其餘worker和window中使用。web

caches上調用 open 方法就能夠異步地獲得一個Cache對象的引用。chrome

Cache.match(request, options)api

返回一個Promise對象,resolve的結果是跟Cache對象匹配的第一個已經緩存的請求。數組

Cache.matchAll(request, options)

返回一個Promise 對象,resolve的結果是跟Cache對象匹配的全部請求組成的數組。

Cache.add(request)

抓取這個URL, 檢索並把返回的response對象添加到給定的Cache對象.這在功能上等同於調用 fetch(), 而後使用 Cache.put() 將response添加到cache中.

Cache.addAll(requests)

抓取一個URL數組,檢索並把返回的response對象添加到給定的Cache對象。

Cache.put(request, response)

同時抓取一個請求及其響應,並將其添加到給定的cache。

Cache.delete(request, options)

搜索key值爲request的Cache條目。若是找到,則刪除該Cache條目,而且返回一個resolve爲true的Promise對象;若是未找到,則返回一個resolve爲false的Promise對象。

Cache.keys(request, options)

返回一個Promise對象,resolve的結果是Cache對象key值組成的數組。

代碼

如下是一個實現離線應用的demo - ServiceWorkerDemo

這個demo是一個簡陋的離線應用,會緩存全部靜態資源請求,即便你修改了index.js和index.css文件,刷新頁面仍是沒有變化。要想看到新的變化,必須更改CACHE_KEY或者修改fetch事件的處理邏輯。

const CACHE_KEY = 'demo';
const CACHE_FILES = [
    '/',
    'bg.jpg',
    'index.js',
    'index.css'
];


self.addEventListener('install', function(event) { // 監聽worker的install事件
    event.waitUntil( // 延遲install事件直至緩存初始化完成
        caches.open(CACHE_KEY)
            .then(function(cache) {
                console.log('Cache created');
                return cache.addAll(CACHE_FILES);
            })
    );
});

self.addEventListener('activate', function(event) { // 監聽worker的activate事件
    event.waitUntil( // 延遲activate事件直到cache初始化完成
        caches.keys().then(function(keys) {
            return Promise.all(keys.map(function(key, i) { // 清除舊版本緩存
                if (key !== CACHE_KEY) {
                    return caches.delete(keys[i]);
                }
            }))
        })
    )
});

self.addEventListener('fetch', function(event) { // 攔截資源請求
    event.respondWith( // 返回資源請求
        caches.match(event.request).then(function(res) { // 判斷是否命中緩存
            if (res) {  // 返回緩存的資源
                return res;
            }
            fallback(event); // 執行請求備份操做
        })
    )
});

function fallback(event) {  // 恢復原始請求
    const url = event.request.clone();
    return fetch(url).then(function(res) { // 請求資源
        //if not a valid response send the error
        if (!res || res.status !== 200 || res.type !== 'basic') {
            return res;
        }

        const response = res.clone();

        caches.open(CACHE_KEY).then(function(cache) { // 緩存從剛剛下載的資源
            cache.put(event.request, response);
        });

        return res;
    })
}

其餘用途

  1. 消息推送
  2. 後臺消息傳遞
  3. 網絡代理,轉發請求,僞造響應
相關文章
相關標籤/搜索