探究 PWA 的實現與應用

PWA(Progressive web apps,漸進式 Web 應用)html

概念

PWA 是現代 web 開發的一個新的理念,他不依賴於某個特定的 API,而是使用各類技術和模式來開發的 web 應用,而且同時具有 web 應用和原生應用的特性,以此來達到最佳 web 體驗的目標;node

一個應用能夠稱爲 PWA,應該具有如下特色:webpack

Discoverable 內容能夠經過搜索引擎發現。nginx

Installable 能夠出如今設備的主屏幕。web

Linkable 你能夠簡單地經過一個URL來分享它。json

Network independent 它能夠在離線狀態或者是在網速不好的狀況下運行。瀏覽器

Progressive 它在老版本的瀏覽器仍舊可使用,在新版本的瀏覽器上可使用所有功能。緩存

Re-engageable 不管什麼時候有新的內容它均可以發送通知。安全

Responsive 它在任何具備屏幕和瀏覽器的設備上能夠正常使用——包括手機,平板電腦,筆記本,電視,冰箱,等。網絡

Safe 在你和應用之間的鏈接是安全的,能夠阻止第三方訪問你的敏感數據。

因此,判斷一個 web 應用是不是 PWA 須要看它是否同時具有原生應用的特性,好比:桌面圖標,離線緩存,消息推送等;固然,他的好處也是不少的,好比:快!真的很是快,而且離線可訪問;用戶能夠贊成添加圖標到主屏方便下次訪問;還能夠實現系統級的消息推送;總之,就是不斷的接近原生應用的體驗!

技術實現

實現一個 PWA 須要的核心技術包括:Service Worker + Manifest.json + HTTPS

Service Worker

Service Worker 是一個註冊在指定資源和路徑下的事件驅動 worker,所以它同其餘類型 worker 同樣不能訪問 DOM,不容許使用同步的 API,好比 localStorage,可是他能攔截並修改訪問的資源請求,經過多種緩存策略來對資源進行緩存和更新;

  • 註冊一個 worker
if ('serviceWorker' in navigator) {
  navigator
  .serviceWorker
  .register('/sw-test/sw.js', { 
    scope: '/sw-test/' 
  })
  .then(function(reg) {
    // registration worked
    console.log('Registration succeeded. Scope is ' + reg.scope);
  }).catch(function(error) {
    // registration failed
    console.log('Registration failed with ' + error);
  });
}
複製代碼
  • 安裝和激活:填充緩存

sw 註冊以後,瀏覽器會嘗試安裝並激活它,安裝完成以後會觸發 install 事件,爲了達到離線緩存的目的,須要使用一個新的存儲 API - caches,這個 APIsw 上的一個全局對象,他能夠用來存儲網絡請求過來的資源,與瀏覽器標準存儲不同的是,他是特定你的域的持久化緩存;

this.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open('v1').then(function(cache) {
      return cache.addAll([
        '/sw-test/app.js'
      ]);
    })
  );
});
複製代碼
  • 控制請求的響應

任何被 sw 控制的的被請求時,都會觸發 fetch 事件,經過監聽該事件能夠控制請求的具體響應內容;

如上,安裝成功後能夠將一批指定的資源緩存起來,那麼如今就能夠攔截請求,而後將匹配到的緩存結果做爲響應,或者從新請求新版的資源,甚至能夠響應指定的內容,你攔截了,那麼你說了算!

// 響應已緩存的請求

this.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request)
  );
});

// 響應自定義內容

const res = new Response('<p>Hello, service worker!</p>', {
  headers: { 'Content-Type': 'text/html' }
});
event.respondWith(res);

// 緩存獲取失敗從新請求最新的

event.respondWith(
  caches.match(event.request).then(function(response) {
    return response || fetch(event.request);
  })
);

複製代碼
  • 更新 Service Worker

若是刷新頁面後有新版的 sw,新版的會在後檯安裝,安裝後並不會當即生效,當沒有頁面在使用舊的版本的 sw 時,新版的就會激活並響應請求;

// 更新到 v2 版本

this.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open('v2').then(function(cache) {
      return cache.addAll([
        '/sw-test/app.js'
      ]);
    })
  );
});
複製代碼
  • 刪除舊緩存

當有了新版本,舊版本還在運行的時候,爲了不緩存數據太多佔滿磁盤空間,須要對舊的緩存進行清理;經過監聽 activate 事件,來對舊的緩存進行清理;

self.addEventListener('activate', function(event) {
  var cacheWhitelist = ['v2'];

  event.waitUntil(
    caches.keys().then(function(keyList) {
      return Promise.all(keyList.map(function(key) {
        if (cacheWhitelist.indexOf(key) === -1) {
          return caches.delete(key);
        }
      }));
    })
  );
});
複製代碼

Manifest.json

該文件裏列出了將應用添加至桌面的全部配置信息,若是修改了該文件,已添加到桌面的應用樣式不會改變,須要從新添加到桌面:

<link rel="manifest" href="/manifest.json" />
複製代碼

例如:

{
  "name": "name",
  "short_name": "short_name",
  "description": "description",
  "icons": [
    {
      "src": "logo.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "logo.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ],
  "share_target": {
      "action": "compose",
      "params": {
        "title": "title",
        "text": "text",
        "url": "url"
    }
  },
  "start_url": "/index",
  "scope": "/",
  "display": "standalone",
  "orientation": "portrait",
  "background_color": "#F3F3F3",
  "theme_color": "#F3F3F3",
  "related_applications": [],
  "prefer_related_applications": false
}
複製代碼

HTTPS

https 是在 http 的基礎上對數據進行加密傳輸,涉及一次非對稱加密與一次對稱加密,即使傳輸過程當中數據被劫持,只要私鑰沒有泄露,黑客也一籌莫展,因此,數據安全性很是高,HTTPS 數據傳輸過程

letsencrypt 是一家爲全球網站免費提供 https 證書的機構,而且支持泛域名,很是值得推薦使用,大公司有錢的請無視;

certbotletsencrypt 官方提供的一個用來獲取和更新 https 證書的工具,certbot.eff.org/ 在它的網站上能夠根據本身的系統及服務狀況,具體選擇如何使用,下面以 CentOS 7Nginx 1.16.0 爲例,看看如何免費爲網站架上 https

  • 獲取 certbot 客戶端
wget https://dl.eff.org/certbot-auto
chmod a+x ./certbot-auto
./certbot-auto --help
複製代碼
  • 配置 nginx

這一步用來驗證域名是否可訪問,後面該工具會在配置的 root 對應目錄下建立臨時的文件,若是不能夠訪問,將無權獲取對應域名的證書;

location ^~ /.well-known/acme-challenge/ {
   default_type "text/plain";
   root     /path/to/www;
}
 
location = /.well-known/acme-challenge/ {
   return 404;
}
複製代碼

別忘了重啓 nginx

sudo nginx -s reload
複製代碼
  • 生成證書
./certbot-auto certonly --webroot -w /path/to/www -d  famanoder.com -d www.famanoder.com 
複製代碼

此處 -w 爲上一步 nginx 裏配置的 root-d 是須要獲取證書的域名,-d 能夠屢次使用,也能夠直接生成泛域名的證書,-d *.famanoder.com,良心之做啊,不少雲服務上不只收費貴,還不支持泛域名,一個域名花一筆錢,此處,大公司有錢的請無視!

  • 自動更新證書

證書生成成功後,能夠選擇是否自動續簽,由於每次生成的證書有效期爲3個月;

./certbot-auto renew --dry-run
複製代碼

經過這個命令能夠測試上一步的自動續簽是否可用;

也能夠手動更新證書:

./certbot-auto renew -v
複製代碼

或者經過命令設置自動更新:

./certbot-auto renew --quiet --no-self-upgrade
複製代碼
  • 使用證書
listen 443 ssl;
server_name .famanoder.com;
index index.html;
root  /path/to/www;
 
ssl_certificate      /etc/letsencrypt/live/famanoder.org/fullchain.pem;
ssl_certificate_key  /etc/letsencrypt/live/famanoder.org/privkey.pem;
複製代碼

可另行查閱更多的如何在 nginx 上配置 https

實戰 PWA

以上對 pwa 作了基本的介紹,以及闡述了一個 pwa 應用所需的基礎設施,接下來使用 offline-plugin 在項目裏實戰 pwa

首先,在咱們應用的入口文件中將其引入,保證當前頁面與 service worker 可以通訊;

import(/* webpackChunkName: "offline" */'offline-plugin/runtime')
.then(offline => {
  offline.install({
    onUpdating: () => {
      console.log('SW Event:', 'onUpdating');
    },
    onUpdateReady: () => {
      console.log('SW Event:', 'onUpdateReady');
      offline.applyUpdate();
    },
    onUpdated: () => {
      console.log('SW Event:', 'onUpdated');
      // window.location.reload();
      // alert('有新版可用,是否刷新?');
    },
    onUpdateFailed: () => {
      console.log('SW Event:', 'onUpdateFailed');
    }
  });
});
複製代碼

該插件會根據 webpack 打包生成的文件,生成 sw.js 文件,配置 webpack

// webpack.config.js

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

module.exports = {
  // ...,
  plugins: [
    // ...,
    new OfflinePlugin({
      ServiceWorker: {
          events: true
      }
    })
  ]
}
複製代碼

這樣,咱們的應用就可以支持 pwa 了,可在控制檯查看相關信息;

使用 pwa 還有很是重要的一點,就是如何更新,如上所述,sw 在安裝新版本後並不會當即激活,大多數時候都須要用戶再一次刷新頁面纔會生效,固然,這個還跟具體的緩存策略有關,目前,觀察一些 pwa 網站,會發現當有新版更新後,網站會經過一個模態框來提醒用戶是否當即刷新頁面使用最新版本,該插件的 runtime 裏提供了 onUpdated 鉤子,當最新版安裝完成後會通知頁面觸發 onUpdated,在這裏咱們能夠調用模態框組件,提醒用戶是否刷新頁面使用最新版;

相關文章
相關標籤/搜索