淺析Service Worker

1、service worker是什麼?

日常瀏覽器窗口中跑的頁面運行的是主JavaScript線程,DOM和window全局變量都是能夠訪問的。html

Service Worker是走的另外的線程,能夠理解爲在瀏覽器背後默默運行的一個線程,或者說是獨立於當前頁面的一段運行在瀏覽器後臺進程裏的腳本。webpack

它脫離瀏覽器窗體,異步地運行在一個徹底獨立的上下文環境,不會對主線程形成阻塞。在service worker中,window以及DOM都是不能訪問的,但可使用self訪問全局上下文。web

2、service worker的做用?

1.離線緩存(重點) 2.消息推送(重點) 3.後臺數據同步 4.響應來自其它源的資源請求, 5.集中接收計算成本高的數據更新,好比地理位置和陀螺儀信息,這樣多個頁面就能夠利6.用同一組數據 7.在客戶端進行CoffeeScript,LESS,CJS/AMD等模塊編譯和依賴管理(用於開發目的) 8.後臺服務鉤子 9.自定義模板用於特定URL模式 10.性能加強,好比預取用戶可能須要的資源,好比相冊中的後面數張圖片chrome

3、service worker的侷限

一、https: Service Worker必須是https協議的,但本地環境下http://localhost或者http://127.0.0.1也能夠的。 二、瀏覽器兼容性: 瀏覽器

image.png

4、service worker的調試

一、chrome://serviceworker-internals 緩存

image.png
二、網頁中Application
image.png

image.png

5、service worker的生命週期

Service Worker生命週期的反應: installing → installed → activating → activated 'install'用來緩存文件,'activate'用來緩存更新 bash

image.png

6、service worker的用法

一、html中異步

if ('serviceWorker' in navigator) {
       // 開始註冊service workers
       navigator.serviceWorker.register('./sw-demo-cache.js', {
           scope: './'
       }).then(function (registration) {
           console.log('註冊成功');
           var serviceWorker;
           if (registration.installing) {
               serviceWorker = registration.installing;
               console.log('安裝installing');
           } else if (registration.waiting) {
               serviceWorker = registration.waiting;
               console.log('等待waiting');
           } else if (registration.active) {
               serviceWorker = registration.active;
               console.log('激活active');
           }
           console.log('=>serviceWorker:', serviceWorker);
           if (serviceWorker) {
               console.log(serviceWorker.state);
               serviceWorker.addEventListener('statechange', function (e) {
                   console.log(' 狀態變化爲', e.target.state);
               });
                // 建立信道
               var channel = new MessageChannel();
               // port1留給本身
               channel.port1.onmessage = e => {
                   console.log('main thread receive message...');
                   console.log(e);
               }
               console.log('給對方', window.RES_MAP);
               // port2給對方
               serviceWorker.postMessage(window.RES_MAP, [channel.port1]);
               serviceWorker.addEventListener('statechange', function (e) {
                   // logState(e.target.state);
               });
           }
       }).catch(function (error) {
           console.log('註冊沒有成功');
       });
   } else {
       console.log('不支持');
   }
複製代碼

二、引進sw-demo-cache.jswordpress

// sw
self.addEventListener('message', ev => {
  console.log('sw receive message..');
  console.log(ev);
  fileMap = ev.data.RES_MAP;
  var arr1 = [].slice.call(fileMap); // ['a', 'b', 'c']
  // 取main thread傳來的port2
  ev.ports[0].postMessage('Hi, hello too');
});

// var fs = require('fs');
// console.log(fs);
// 緩存
self.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open(VERSION).then(function(cache) {
      return cache.addAll([
        './index.html',
      ]);
    })
  );
});

// 緩存更新
self.addEventListener('activate', function(event) {
  console.log('two now ready to handle fetches!');
  event.waitUntil(
    caches.keys().then(function(cacheNames) {
      return Promise.all(
        cacheNames.map(function(cacheName) {
          console.log('cacheName:', cacheName);
          // 若是當前版本和緩存版本不一致
          if (cacheName !== VERSION) {
            return caches.delete(cacheName);
          }
        })
      );
    })
  );
});

// 捕獲請求並返回緩存數據
self.addEventListener('fetch', function (event) {
  try{
    event.respondWith(
        caches.match(event.request).then(function(res){
            if(res){
                return res;
            }
            requestBackend(event);
        })
    )
  } catch {
    console.log(event);
  }
});

function requestBackend(event){
  var 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;
      }
      var response = res.clone();
      console.log('VERSION:', VERSION);
      caches.open(VERSION).then(function(cache){
          cache.put(event.request, response);
      });

      return res;
  })
}
複製代碼

三、webapck中獲取文件目錄 引入第三個模塊glob,遞歸獲取打包後的文件目錄post

exports.resMap = function () {
    var entryFiles = glob.sync(PAGE_PATH + '/*/*.js')
    var map = {}
    entryFiles.forEach((filePath) => {
        var filename = filePath.substring(filePath.lastIndexOf('\/') + 1, filePath.lastIndexOf('.'))
        map[filename] = filePath;
    })
    var entryFiles2 = glob.sync(PAGE_PATH2 + '/*')
    var map2 = {}
    findPath(entryFiles2, map2);
    console.log('map2', map2);
    return map2;
};


function findPath(entryFiles2, map2) {
    entryFiles2.forEach(filePath => {
        var filename = filePath.substring(filePath.lastIndexOf('/') + 1, filePath.lastIndexOf('.'));
        if (filePath.indexOf('.') <= 0) {
            let pathRes = path.resolve(__dirname, filePath);
            let files = glob.sync(pathRes + '/*');
            findPath(files, map2);
            map2[filename] = filePath;
        }
        map2[filename] = filePath;
    });
}
複製代碼

四、導出目錄 經過webpack的DefinePlugin插件,導出上步獲取的目錄 五、web和service worker的通訊 經過postMessage實現web和service worker間的通訊

// 建立信道
                var channel = new MessageChannel();
                // port1留給本身
                channel.port1.onmessage = e => {
                    console.log('main thread receive message...');
                    console.log(e);
                }
                console.log('給對方', window.RES_MAP);
                // port2給對方
                serviceWorker.postMessage(window.RES_MAP, [channel.port1]);
                serviceWorker.addEventListener('statechange', function (e) {
                    // logState(e.target.state);
                });
複製代碼
// sw
self.addEventListener('message', ev => {
  console.log('sw receive message..');
  console.log(ev);
  fileMap = ev.data.RES_MAP;
  var arr1 = [].slice.call(fileMap); // ['a', 'b', 'c']
  // 取main thread傳來的port2
  ev.ports[0].postMessage('Hi, hello too');
});
複製代碼

小結

一、service worker讓離線緩存成爲可能,offline狀況下也能夠訪問頁面。然而銷燬比較困難,更新會有問題。目前只能chrome://serviceworker-internals手動銷燬,還待研究。

參考: 藉助Service Worker和cacheStorage緩存及離線開發 Service Worker 簡介

相關文章
相關標籤/搜索