PWA 學習

傳統一般Web 應用程式所缺少的核心屬性:

  • 可靠性 :如果網頁的載入時間超過 3 秒,Google 發現 53% 的用戶放棄了這個網站
  • 快速:網頁的頁面切換和滑動在手機上不如原生應用程式來的平順
  • 響應 ( Responsive ) : 適應不同裝置的螢幕大小 ( 這部分其實有很多 CSS 框架開始支援,例如 Bootstrap )
  • 可安裝性:像原生應用程式一樣安裝,然後使用者可以直接使用應用程式程式啟動畫面 ( Splash Screen )
  • 高度參與 ( Highly engage-able ) :例如通知功能,在手機螢幕 Home icon ,全螢幕支援,可離線

PWA 是獨立於 ReactJS,Angular ,VueJS 這些最新的網頁前端框架,沒有相依關係

Progressively enhance web apps to look and feel like native apps.
* Be reliable: Load fast and provide offline functionality.
* Fast: Respond quickly to user actions.
* Engaging: Feel like a native app on mobile devices.

PWA V.S Mobile Web

  • Native Apps
    • Push Notifications bring Users back.
    • Home Screen Icons make Access Easy
    • Access Native Device Features like Camera
    • Possibly work offline

PWA 主要組成元件

PWA 的技術元件組成 – Service Worker/Manifest.json/HTTPS ( 很像在開發一個 Chrome extension )
組成元件

pwa 核心元件圖

Service workers

  • 類似飛機的塔台管制員,決定是要從網路或是本地端取得資源。透過攔截HTTP Request,進而完全控制網站行為
  • 使用Javascript寫成,但與javascript運行在不同的執行緒,不會互相干擾,運行在它自己的全域腳本上下文中。
  • 只能使用 HTTPS
  • 不綁定到具體的網頁
  • 無法修改網頁中的元素,因為它無法訪問 DOM
<html>
  <head>The best web page ever</head>
  <body>
    <img src="/images/hello.png" />  <!-- Image -->
    <script async src="/js/script.js"></script>
  <script>
    // 檢查目前瀏覽器是否支援service worker同時註冊 service worker
    // sw.js 為 service worker文件
    if ('serviceWorker' in navigator) {
      navigator.serviceWorker.register('/sw.js').then(function(registration) {
        // 注册成功
        console.log('ServiceWorker registration successful with scope: ', registration.scope);
      }).catch(function(err) {                                                                                 // 注册失败 :(
        console.log('ServiceWorker registration failed: ', err);
      });
    }
  </script>
  </body>
</html>
  • sw.js
// 安裝階段 Install
var cacheName = 'helloWorld';
self.addEventListener('install', event => {
  event.waitUntil(
    caches.open(cacheName)
    .then(cache => cache.addAll([
      '/js/script.js',
      '/images/hello.png'
    ]))
  );
});

// 監聽fetch事件
self.addEventListener('fetch', function (event) {
  event.respondWith(
    caches.match(event.request)  // 是否緩衝有資料
    .then(function (response) {
      if (response) {            // 有資料
        return response;
      }
      return fetch(event.request);
    })
  );
});

以上簡單程式碼就完成了PWA的基本服務,當使用者重整網頁後,就可以體驗到由PWA提供的快速頁面。但是對於新的資訊取得卻無法事先定義,所以可以改進為下列方式:
* sw.js

var cacheName = 'helloWorld';
self.addEventListener('fetch', function (event) {
  event.respondWith(
    caches.match(event.request)
    .then(function (response) {
      if (response) { 
        return response;
      }
      var requestToCache = event.request.clone();
      return fetch(requestToCache).then( 
        function (response) {
          if (!response || response.status !== 200) { // 如果取得失敗,就用cache資料
            return response;
          }
          var responseToCache = response.clone();
          caches.open(cacheName)
            .then(function (cache) {
              cache.put(requestToCache, responseToCache); // 將請求添加到response中
            });
          return response;
        }
      );
    })
  );
});

其他參數

  • 忽略網址上的?後面的異動: ignoreSearch: true
self.addEventListener('fetch', function (event) {
  event.respondWith(
    caches.match(event.request, {
      ignoreSearch: true
    }).then(function (response) {
      return response || fetch(event.request);
    })
  );
});

上面的方式對於靜態網頁或是變動不大的頁面會非常適用,但對於即時異動的頁面(如:金融網頁、體育賽事等)卻不太適合。因此,可以對回應內容加入版本控制:

適合動態網頁的處理方式

web 應用程式清單

  • manifest.json
{
  "name": "Progressive Times web app",
  "short_name": "Progressive Times",   // 主畫面的名稱
  "start_url": "/index.html",
  "display": "standalone",
  "theme_color": "#FFDF00",   // 整個APP的顏色
  "background_color": "#FFDF00",
  "icons": [{
      "src": "homescreen.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "homescreen-144.png",
      "sizes": "144x144",
      "type": "image/png"
    }
  ]
}
  • 需要在 index.html 中宣告
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Progressive Times</title>
    <link rel="manifest" href="/manifest.json">  ❶
  </head>
  <body>
    ..content goes here..
  </body>
</html>

添加到主螢幕功能

  • 須滿足下列條件
    • 需要有 manifest.json 文件
    • 清單文件需要啟動URL
    • 需要 144×144 png圖檔
    • 需要使用 https 傳輸協定及啟動service worker
    • 用戶至少需要訪問網站兩次以上,訪問間隔五分鐘以上,提示才會出現
"name": "Progressive Times web app",
"theme_color": "#FFDF00",
"background_color": "#FFDF00",
"icons": [
  {
    "src": "homescreen.png",
    "sizes": "144x144",
    "type": "image/png"
  },
"start_url": "/index.html",
"display": "standalone",  // Fullscreen, Standalone, Minimal-ui,Browser

取消添加到主螢幕的提示

window.addEventListener('beforeinstallprompt', function(e) {
  event.userChoice.then(function (result) {
    console.log(result.outcome);
    if (result.outcome == 'dismissed') {
      // 发送数据以进行分析
    } else {
      // 发送数据以进行分析
    }
  });
});

UI shell

  • 用戶屆面所必須最小化的HTML、CSS 與 Javascript。
  • 一但UI shell加載完成,網站的動態內容就會使用Javascript來獲取並加載。

從現有的網站轉移到PWA網站

pwa flow

Analysis

改進

  • Optimize images
    • 影像的功能:
      • navigation and action
      • branding and priority
// Precache
const manifest = [{images/priority/', ....}];
workboxSW.precache(manifest);
 - decorative
// Cache at runtime(with limit)
// Runtime cache strategy
workboxSW.strategies.cacheFirst({
   cacheName: 'images-cache',
     cacheExpiration: {maxEntries: 50 },
});
 - informative
  • Remove unnecessary code
  • Leverage browser caching
  • Avoid blocking code
  • Divide big, monolithic Javascript

如何建立PWA?

  • Gereric Tools
  • Framework-Specific Tools
  • Full Custom

  • Workbox

    • Offline caching
    • Offline analytics
    • Background sync
    • 工具 ponterest/service-workers

檢測工具

快速上手工具

參考資料