來看看Service Worker的項目實戰

Progressive Web App, 簡稱 PWA,是提高Web App的體驗的一種新方法,能給用戶原生應用的體驗,致力於用前沿的技術開發,讓網頁使用如同原生App般的體驗的一系列方案javascript

用來自Google Developers的解答Progressive Web Apps:css

  • 漸進式 - 適用於全部現代瀏覽器,由於它是以漸進式加強做爲宗旨開發的
  • 離線使用 - 藉助 Service Worker 可以在離線或者網絡較差的狀況下正常訪問
  • 可安裝 - 用戶能夠添加到桌面並生成快捷方式,一鍵訪問
  • 相似應用 - 因爲是在 App Shell 模型基礎上開發,由於應具備 Native App 的交互和導航,給用戶 Native App 的體驗
  • 持續更新 - 始終是最新的,無版本和更新問題
  • 安全 - 經過 HTTPS 協議提供服務,防止窺探和確保內容不被篡改
  • 可索引 - 應用清單文件和 Service Worker 可讓搜索引擎索引到,從而將其識別爲『應用』
  • 粘性 - 網頁已經關閉的狀況下還能夠經過推送後臺通知等,讓用戶迴流

咱們也能夠經過一個DEMO看看實際效果=>天氣 PWAhtml

而其中,PWA方案的最主要核心功能都是依賴於Service Worker這個API來實現的.前端


Service Worker是什麼呢?

W3C 組織早在 2014 年 5 月就提出過 Service Worker這樣的一個 HTML5 API ,主要用來作持久的離線緩存。html5

固然這個 API 不是憑空而來,至於其中的由來咱們能夠簡單的捋一捋:java

瀏覽器中的javaScript 都是運行在一個單一主線程上的,在同一時間內只能作一件事情。隨着 Web 業務不斷複雜,咱們逐漸在 js 中加了不少耗資源、耗時間的複雜運算過程,這些過程致使的性能問題在 WebApp 的複雜化過程當中更加凸顯出來。webpack

W3C 組織早早的洞察到了這些問題可能會形成的影響,這個時候有個叫Web Worker 的 API 被造出來了,這個 API 的惟一目的就是解放主線程,Web Worker 是脫離在主線程以外的,將一些複雜的耗時的活交給它幹,完成後經過 postMessage 方法告訴主線程,而主線程經過 onMessage 方法獲得 Web Worker 的結果反饋。git

一切問題好像是解決了,但 Web Worker 是臨時(即瀏覽器關閉後就關閉了)的,咱們能不能有一個東東是一直持久存在的,而且隨時準備接受主線程的命令呢?基於這樣的需求推出了最第一版本的 Service WorkerService WorkerWeb Worker 的基礎上加上了持久離線緩存能力.github

Service Worker 有如下功能和特性:web

  • 一個獨立的 worker 線程,獨立於當前網頁進程。
  • 一旦被 install,就永遠存在,除非被 uninstall
  • 須要的時候能夠直接喚醒,不須要的時候自動睡眠
  • 可編程攔截代理請求和返回,緩存文件,緩存的文件能夠被網頁進程取到(包括網絡離線狀態)
  • 離線內容開發者可控
  • 能向客戶端推送消息
  • 不能直接操做 DOM
  • 出於安全的考慮,必須在 HTTPS 環境下才能工做

因此咱們基本上知道了 Service Worker 的偉大使命,就是讓緩存作到優雅和極致,讓 Web App 相對於 Native App 的缺點更加弱化,也爲開發者提供了對性能和體驗的無限遐想。

Service Worker工做原理

Service Worker的技術核心是Service Worker腳本,它 是一種由Javascript編寫的瀏覽器端代理腳本。

前端頁面向內核發起註冊時會將腳本地址通知內核,內核會啓動獨立進/線程加載Service Worker腳本並執行Service Worker安裝及激活動做。成功激活後便進入空閒等待狀態,若當前的Service Worker進/線程一直沒有管轄的頁面或者事件消息時會自動終止(具體的終止策略視不一樣瀏覽器及版本而定,不會影響前端編寫邏輯,但前端勿在Service Worker腳本中保存須要持久化的信息,能夠藉助localstorage),當打開新的可管轄頁面或者已管轄頁面發起message等消息時,Service Worker進/線程會被從新喚起。

每當已安裝的Service Worker有管轄頁面被打開時,便會觸發Service Worker腳本更新,當Service Worker腳本發生了更改,便會忽略本地網絡cacheService Worker腳本直接從網絡拉取。若網絡拉取的與本地有一個字節的差別都會觸發Service Worker腳本的更新,更新流程與安裝相似,只是在更新安裝成功後不會當即進入active狀態,須要等待舊版本的Service Worker進/線程終止。

實例代碼

// 在html裏註冊service-worker
if (navigator.serviceWorker != null) {
    navigator.serviceWorker.register('sw.js')
      .then(function(registration) {
        console.log('Registered events at scope: ', registration.scope);
      });
  }	
複製代碼
// 首先定義須要緩存的路徑, 以及須要緩存的靜態文件的列表。
var cacheStorageKey = 'minimal-pwa-8'

var cacheList = [
  '/',
  "index.html",
  "main.css",
  "e.png",
  "*.png"
]

// 藉助 Service Worker, 能夠在註冊完成安裝 Service Worker 時, 抓取資源寫入緩存:
window.addEventListener('install', function(e) {
  console.log('Cache event!')
  e.waitUntil(
    caches.open(cacheStorageKey).then(function(cache) {
      console.log('Adding to Cache:', cacheList)
      return cache.addAll(cacheList) 
    }).then(function() {
      console.log('Skip waiting!')
      return self.skipWaiting()
    })
  )
})

// 網頁抓取資源的過程當中, 在 Service Worker 能夠捕獲到 fetch 事件, 能夠編寫代碼決定如何響應資源的請求:
window.addEventListener('fetch', function(e) {
  // console.log('Fetch event:', e.request.url)
  e.respondWith(
    caches.match(e.request).then(function(response) {
      if (response != null) {
        console.log('Using cache for:', e.request.url)
        return response
      }
      console.log('Fallback to fetch:', e.request.url)
      return fetch(e.request.url)
    })
  )
})
複製代碼

關於事件

install 事件:當前Service Worker腳本被安裝時,會觸發 install 事件。

push事件: push 事件是爲推送通知而準備的。不過首先你須要瞭解一下 Notification API PUSH API

經過 PUSH API,當訂閱了推送服務後,可使用推送方式喚醒 Service Worker 以響應來自系統消息傳遞服務的消息,即便用戶已經關閉了頁面。

online/offline事件: 當網絡狀態發生變化時,會觸發 onlineoffline 事件。結合這兩個事件,能夠與 Service Worker 結合實現更好的離線使用體驗,例如當網絡發生改變時,替換/隱藏須要在線狀態才能使用的連接導航等。

fetch 事件: 當咱們安裝完Service Worker成功並進入激活狀態後即運行於瀏覽器後臺,咱們的這個線程就會一直監控咱們的頁面應用,若是出現HTTP請求,那麼就會觸發fetch事件,而且給出本身的響應。 這個功能是十分強大的,藉助 Fetch APICache API 能夠編寫出複雜的策略用來區分不一樣類型或者頁面的資源的處理方式。它可以提供更加好的用戶體驗: 例如能夠實現緩存優先、降級處理的策略邏輯:監控全部 http 請求,當請求資源已經在緩存裏了,直接返回緩存裏的內容;不然使用 fetch API 繼續請求,若是是 圖片或 css、js 資源,請求成功後將他們加入緩存中;若是是離線狀態或請求出錯,則降級返回預緩存的離線內容。


使用webpack插件

看到這裏不少人會有疑問了,既然能夠經過service-worker緩存資源,那若是一個正式項目,在項目迭代後,並將代碼推送到正式環境後,前端怎麼實時知道並從新緩存新的資源呢?

​ 第一種方式,就是每次修改都手動去更改sw文件的版本號,觸發更新。

​ 第二種就是使用webpack插件自動化處理

事實上,在咱們真實的用webpack生成的項目中,若是按照第一種方式手動去寫Service-worker.js文件的話,會遇到兩個問題:

  1. webpack生成的資源多會生成一串hash,Service-worker.js的資源列表裏面須要同步更新這些帶hash的資源;
  2. 每次更新代碼,都須要經過更新service-worker文件版本號來通知客戶端對所緩存的資源進行更新.

看到這裏就該讓用webpack插件:offline-plugin 登場了,官方同時也推薦sw-precache-webpack-plugin ,offline-plugin不只可以解決剛剛那個提到的緩存更新的問題,同時還具有如下的優勢:

  • 一、自動生成和更新Service-worker.js文件和自動爲SW添加緩存資源列表
  • 二、更爲詳細的文檔和例子;
  • 三、迭代頻率相對更高,star數更多;
  • 四、自動處理生命週期,用戶無需糾結生命週期的坑;
  • 五、支持自動生成manifest文件。

部署到項目中也十分的簡單

1.安裝

npm install offline-plugin [--save-dev]
複製代碼

2.初始化

第一步,進入webpack.config.js:

// webpack.config.js example

var OfflinePlugin = require('offline-plugin');

module.exports = {
  // ...

  plugins: [
    // ... other plugins
    // it's always better if OfflinePlugin is the last plugin added
    new OfflinePlugin()
  ]
  // ...
}
複製代碼

3.入口文件導入

import * as OfflinePluginRuntime from 'offline-plugin/runtime';
OfflinePluginRuntime.install();
複製代碼

通過上面的步驟,offline-plugin已經集成到項目之中,經過webpack構建便可。

具體代碼也可查看 demo


PWA的瀏覽器支持狀況

兼容性查看

畢竟是自家產品,chrome瀏覽器確定是支持度最高的瀏覽器,chrome64版本是基本支持全部PWA功能API的。

但國內的瀏覽器·支持狀況相對差一些,並且chrome移動版的使用人羣仍是偏少的,不過在UC的支持程度也不低

相關文章
相關標籤/搜索