構建離線web應用(一)

我喜歡移動app,並且也是那些堅持使用Web技術構建移動應用程序的人之一。javascript

通過技術的不斷迭代(可能還有一些其它的東西),移動體驗設計越來越平易近人,給予用戶更好的體驗。css

而今天,咱們就要介紹一個新技術--漸進式 web 應用程序。在理解這個概念並本身嘗試了一下以後,我以爲沒有必要再作 hybrid 應用了。html

咱們準備作這樣的一個demo:
demo預覽java

Progressive Web Apps

漸進式 Web 應用是典型的旨在提升用戶離線體驗的 Web 應用。它解決了這樣的問題:怎麼才能不顯示相似下面的離線錯誤?
離線errorjquery

事實上,PWA 不只解決了離線錯誤,還在恢復鏈接的時候將用戶與內容鏈接起來。移動設備是漸進式 web 應用的主要使用場景。讓我來告訴你爲何?web

桌面瀏覽器

  • 用戶打開電腦(在家、學校或者辦公室)chrome

  • 檢查是否連上網絡,沒有則手動鏈接shell

  • 打開 web 應用api

移動端瀏覽器

  • 拿出手機promise

  • 默認手機已經鏈接上網絡

  • 直接打開 app

如上,用戶對待兩種場景的處理方式是不同的。移動端用戶不必定有很好的網絡鏈接,有的甚至沒有。在這樣的場景下,開發商須要作的就是保持用戶對產品的好感,在其網絡恢復時與其互動。若是信號不好,開發商須要經過一些手段保持用戶的耐心,不至於在請求過程當中用戶直接關閉 web 應用。

當咱們開始構建 PWA 應用時,你就能理解上面的場景了。

Service Workers

PWA 背後的原理是 service workers。若是想讓用戶在離線場景下依然保持打開 web 頁面,你須要在用戶打開 web 應用而且有網絡鏈接時作一些「後臺任務」,這個「後臺任務」會蒐集 web 頁面最近一次運行須要的一些資源,以備離線時使用。

這就好像每一年秋收儲備糧食,以備冬天不時之需同樣,不斷循環。

PWA 中的 service worker,能夠類比成春天的播種的農民。下面是 MDN 對 service workers 的描述:

Service worker 是一個註冊在指定源和路徑下的事件驅動 worker。它採用 JavaScript 控制關聯的頁面或者網站,攔截並修改訪問和資源請求,細粒度地緩存資源。你能夠徹底控制應用在特定情形(最多見的情形是網絡不可用)下的表現。

簡而言之,service worker 就是一些在後臺運行邏輯的 worker。它沒有權限操做 DOM,可是能夠調用其它的 API (例如 IndexDB 以及 Fetch API)。

開始以前請牢記:

  • service workers 只能在 HTTPS 協議下生效(或者 Localhost)。

  • service workers 被設計成異步的,不能使用 XHR (但你可使用 Fetch)或者 LocalStorage。

  • service workers 的做用範圍是針對相對路徑的。所以,demo/sw.js 只能相對於 demo 起做用,demo/first/sw.js 相對於 first

Mobile 仍是 PWA

若是你能利用 service workers 存儲離線使用所需的文件,那你就沒有必要開發移動 app 了。若是你的 web 應用對移動用戶進行了優化,而且幾乎不須要調用移動端的硬件功能,那麼你應該嘗試一下 PWA。

我花了一些時間看飛行模式下一些移動 app 的表現。我將它們分紅三類:

離線狀況下不作任何操做

例子: Coinbase
Coinbase

Coinbase 就是一直停留在 loading 的這個頁面。它甚至讓我懷疑這樣的 app 爲啥要存在,由於這個頁面簡直跟 web 展現如出一轍。Coinbase 不是財經類 app,無需實時展現信息,所以,PWA 可能只適用應用於其 App Shell。

App Shell 是指不包含動態內容的一部分應用程序。例如導航菜單、側邊欄、背景、logo 等等。

離線狀況下展現警告信息(未鏈接網絡等等),展現 App Shell,但其它都不可用

例子:Uber

Uber

Uber 給用戶展現了一些信息(經過 App Shell 以及地圖),而且告知用戶不能操做是因爲他網絡中斷了。Uber是一個很高頻的 app,這樣的交互展現對於他們的應用場景頗有意義。

離線狀況下展現緩存的數據

例子: Medium

Medium

Medium在離線狀態下展現緩存的數據,一些離線展現在這個分類裏面的 app(例如,Instagram)還會提示用戶離線了,因此,就不要對這個分類裏面的 app 指望再搞了。

優化

個人想法是,若是 PWA(或者 service workers)技術成熟而且被大規模應用的話,爲何不節省掉:

  1. 前往應用商店

  2. 下載並不經常使用的 app

呢?

當咱們接下來談到 Web Manifest 時,你就意識到只要給你的 web 應用新增一個桌面 icon,web 應用就能夠經過點擊這個 icon 實現啓動了。

一些公司已經在 PWA 方面作的比較好了,你能夠在這個網址上面找到這些公司:pwa.rocks

開發準備

咱們已經介紹了足夠多的理論知識了。這是一個手把手的教程,來吧,讓咱們動起手來。首先,按照下面的結構來建立一個新的項目:

|--pwa-demo
|----css
|----fonts
|----images
|----js
|----index.html
|----service-worker.js

下載 Materialize 這個 UI 庫,用裏面 CSSFontsjs 文件分別替換項目裏面的文件夾。

打開 index.html 文件,引入一些資源:

<!-- ./index.html -->
<!DOCTYPE html>
  <html>
    <head>
      <!--Import Google Icon Font-->
      <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
      <!--Import materialize.css-->
      <link type="text/css" rel="stylesheet" href="css/materialize.min.css" media="screen,projection"/>
      <link type="text/css" rel="stylesheet" href="css/app.css">

      <!--Let browser know website is optimized for mobile-->
      <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    </head>

    <body>

      Body coming soon

      <!-- Scripts -->
      <script type="text/javascript" src="js/jquery-2.1.1.min.js"></script>
      <script type="text/javascript" src="js/materialize.min.js"></script>
      <script type="text/javascript" src="js/app.js"></script>
    </body>
  </html>

咱們已經引入了下載好的文件,還須要本身在相應的目錄建立一下 app.css 以及 app.js 這兩個文件。

註冊 Service Worker

越早在瀏覽器註冊,Service Worker 就能越早的開始工做。最佳的作法是在應用的入口。在這個項目中,咱們能夠在 app.js 註冊一個新的 worker:

(function(){
  if ('serviceWorker' in navigator) {
    navigator.serviceWorker
     .register('/service-worker.js')
     .then(function() { 
        console.log('Service Worker Registered'); 
      });
  }    
})()

在作其餘操做以前,咱們首先須要檢測一下瀏覽器對於 Service Worker 的兼容性。若是支持,那咱們就能夠利用 register 這個方法來註冊這個 worker,這個方法告知了 service worker 文件的路徑。註冊函數返回一個 promise ,你能夠在這個 promise 裏面判斷註冊是否成功。

Service Worker 週期

在開始構建 PWA 以前,你須要理解 Service Worker 的生命週期:

Install

這一階段主要是讓 worker 在瀏覽器給定的做用域掛載。因爲這是生命週期的第一步,最好在這一步緩存各類資源:

// ./service-worker.js

var cacheName = 'PWADemo-v1';
var filesToCache = [
  '/index.html',
  '/css/app.css',
  '/js/app.js',
  /* ...and other assets (jQuery, Materialize, fonts, etc) */
];

self.addEventListener('install', function(e) {
  console.log('[ServiceWorker] Install');
  e.waitUntil(
    caches.open(cacheName).then(function(cache) {
      console.log('[ServiceWorker] Caching app shell');
      return cache.addAll(filesToCache);
    })
  );
});
  • caches.opencache.addAll 都是異步操做.service worker 在這些操做完成以前可能會中斷,e.waitUntil用來等待 promise 的狀態變成 resolved 或者 rejected。

  • 當緩存開關被打開時,咱們嘗試利用 addAll 來新增緩存。

  • 請記住,只要有一個文件緩存失敗,service worker 就沒法被正確掛載。

Activate

當 worker 掛載完成,其效果並不會當即展現出來,除非前一個 service worker 銷燬而且該 web 應用被從新訪問。假設咱們掛載了另外一個不一樣 cacheName 的 service worker:

// ./service-worker.js

var cacheName = 'PWADemo-v2';
var filesToCache = [
  //...
];

self.addEventListener('install', function(e) {
  console.log('[ServiceWorker] Install');
  //...
});

當這個新的 service worker 建立以後,新的緩存 PWADemo-v2 也被建立,這時候 PWADemo-v1 仍然存在。當觸發 Activate 時,咱們能夠刪除 PWADemo-v1,使其「讓位」於 PWADemo-v2

// ./service-worker.js

self.addEventListener('activate', function(e) {
  console.log('[ServiceWorker] Activate');
  e.waitUntil(
    caches.keys().then(function(keyList) {
      return Promise.all(keyList.map(function(key) {
        if (key !== cacheName) {
          console.log('[ServiceWorker] Removing old cache', key);
          return caches.delete(key);
        }
      }));
    })
  );
});

咱們檢查全部的 cache 名稱,若是發現不是正在使用的 cache,那麼將其直接刪除。

Fetch

Fetch 不是一個必需的生命週期,但它提供了攔截請求資源的方法。當發送請求時,首先會觸發這樣的事件:

// ./service-worker.js

self.addEventListener('fetch', function(e) {
  console.log('[ServiceWorker] Fetch', e.request.url);
  e.respondWith(
    caches.match(e.request).then(function(response) {
      return response || fetch(e.request);
    })
  );
});

若是資源已經被緩存了,咱們返回瀏覽器緩存的版本。若是沒有,那麼咱們調用 fetch api 去發送 HTTP 請求該資源。

Debuggering Service Workers

因爲 service workers 的工做方式,特別是進行緩存時,不是很容易進行 debugger 調試。幸運的是,chrome 的 dev tools 提供了助力。跟着下面的步驟,調試咱們剛註冊的 service worker:

  • 打開 chrome dev tools

  • 點擊 Application 這一選項,打開 service worker 分區:

Chrome dev tools

  • 你能夠查看到 status 是綠色的,這就代表你的 service worker 成功了:

status

  • 你能夠打開 "Update on reload" 去強制更新 service worker,不用關閉全部已存在的 session:

update on reload

  • 右擊 "Cache Storage",而後點擊刷新去查看緩存。根據名稱點擊你所設置的cache,而後你就會看到緩存裏面的各個項:

Cache storage

接下來

你已經瞭解了必備的知識點,PWA 的概念對你來講已經不陌生了。接下來,咱們將要討論 PWA 的緩存策略。咱們將瞭解如何使用 IndexDB 來保存數據而不是 localStorage。

相關文章
相關標籤/搜索