學習使用 PWABuilder 建立 PWA App 心得

Progress Web Apps on Windows 在 Microsoft Build 2018 被提及,隨著更多公司(ex: Twitter, Uber 等)把服務轉成 PWA 上架到 Store,讓 PWA 更加熱門。

本篇介紹 PWABuilder 使用過程遇到的問題與心得。

Progress Web Apps(PWAs) 讓開發人員沿用 Web 技術做到接近 native app-like experience,省去需要開發不同平臺的成本。

但 PWA 最大難度就是漸進式把 Web site 功能效果做到接近 native apps 的品質。

根據 Progress Web Apps on Windows 的介紹,要做到好的 PWA apps,需要滿足下圖特點:

支援 offline 跟 push notification 是 PWAs 最基本的需求,進一步則是可從 web search 找到内容並開啓 App 或是分享連結來操作。

PWA Builder 幫忙,讓現有的網站容易變成 App 上架到 Store。

往下介紹參考 Generate your Progressive Web App 如何建立 PWA App,並補充需注意的地方:

  • 輸入特定 URL 之後,PWABuilder 會解譯出網站的説明,並顯示需要調整的地方,最常見的問題就是:missing image:
    雖然不影響封裝,但建議所有圖片都提供省去後面封裝檔案缺圖的問題;
  • 在建立 Manifest 時可以搭配 Web App Manifest 來寫入必要的資訊,其中比較重要的:
    • nameshort_name:代表 Web Application 的名稱。
    • scope:代表可以瀏覽的範圍,如果沒有填寫會以 start_url 的目錄爲主。例如: start_url 是 /page/welcome.html 那 scope 可以存取 /page/ 以下的内容;詳細可參考 4. Navigation scope
    • display:代表如何呈現在系統上,具有:fullsreen, standalone, minimal-ui, browser。
    • start_url:預設為用戶啓動 web application 要顯示的 URL。
    • icons:圖片的集合,與 PWA 要被安裝在那個平臺有關係,可以利用 App Image Generator 建立各平臺的圖示。
    • serviceworker:定義 service worker 的詳細資訊,例如:
      "serviceworker": {
        "src": "sw.js",
        "scope": "/foo",
        "update_via_cache": "none"
      }
    定義好的 manifest.json 可以加入到網站的宣告: <meta src="manifest.json" />,方便 PWABuilder 下載,而 Browser 也會參考 manifest.json 的定義安裝 web application。如果安裝成功,在設備的 home screen 就會看到該 web application。
  • Service Workers
    它是讓 browser 可在背景運作的 scripts,在 browser 中利用程式管理 web/HTTP request 作爲 network proxy。 利用下面的範例程式把 service workers 注冊到您的網站:
    if (navigator.serviceWorker.controller) {
      console.log('[PWA Builder] active service worker found, no need to register')
    } else {
      //Register the ServiceWorker
      navigator.serviceWorker.register('pwabuider-sw.js', {
        scope: './'
      }).then(function(reg) {
        console.log('Service worker has been registered for scope:'+ reg.scope);
      });
    }
    [注意]
    • 可利用 is ServiceWorker ready 或是 if('serviceWorker' in navigator) 來檢查瀏覽器是否支援
    • 只支援 HTTPS 或 localhost
    Service Worker 的生命周期: service worker lifecycle 從上圖可理解注冊 Service Worker 之後,完成 install 後會進入 active 並在背景運作,如果過程失敗它將不會被啓動,但再下一次進去這個網站時會執行一次。
    //This is the "Offline page" service worker
    
    //Install stage sets up the offline page in the cache and opens a new cache
    self.addEventListener('install', function(event) {
      var offlinePage = new Request('offline.html');
      event.waitUntil(
        fetch(offlinePage).then(function(response) {
          return caches.open('pwabuilder-offline').then(function(cache) {
            console.log('[PWA Builder] Cached offline page during Install'+ response.url);
            return cache.put(offlinePage, response);
          });
      }));
    });
    
    //If any fetch fails, it will show the offline page.
    //Maybe this should be limited to HTML documents?
    self.addEventListener('fetch', function(event) {
      event.respondWith(
        fetch(event.request).catch(function(error) {
          console.error( '[PWA Builder] Network request Failed. Serving offline page ' + error );
          return caches.open('pwabuilder-offline').then(function(cache) {
            return cache.match('offline.html');
          });
        }
      ));
    });
    
    //This is a event that can be fired from your page to tell the SW to update the offline page
    self.addEventListener('refreshOffline', function(response) {
      return caches.open('pwabuilder-offline').then(function(cache) {
        console.log('[PWA Builder] Offline page updated from refreshOffline event: '+ response.url);
        return cache.put(offlinePage, response);
      });
    });
    如上面範例,在 install 完畢之後則加入一個 offline.html 到 cache 裏面。 在 active 狀態時,Service Worker 將控制 scope 下 pages 的交易,因此,當有 http request 出現時會觸發 fetch 事件,可以搭配 event.responseWith() 先檢查是否有 caches ,如果有就可以直接回復,如果沒有才真的轉給 server。如下範例:
    self.addEventListener('fetch', function(event) {
      event.respondWith(
        caches.match(event.request)
          .then(function(response) {
            // Cache hit - return response
            if (response) {
              return response;
            }
            return fetch(event.request);
          }
        )
      );
    });
    如果使用 PWABuilder 它會根據您選擇的 Service Worker 需要的功能 (例如: offline page, offline copy of pages, offline copy with backup offline pages, cache-first network 或 advanced pre-cache)產生必要的 js 檔案,記得把它放到您的網站。 更多 Service Worker 的介紹可以參考 Service Worker

利用 PWABuilder Web tool 產生 manifest.xml 與選擇 Service Worker 支援的功能後,選擇建立 packages 或是把專案下載下來,如下圖: 如果您選擇 appx 的下載可以從 zip 中找到 windows.appx,如果不是則需要自己編譯,下面來看有那些步驟: 目錄為:

{app name}-windows10
->\projects
  ->\PWA
    ->\Store packages
      ->\windows10
        ->\manifest (封裝成 appx 必要的 appxmanifest.xml 與相關圖片)
        ->\package (封裝成品的目錄)
        ->\source (專案的 source code, 裏面有 package.appxmanifest 與相關圖片)
        ->\generationInfo.json
        ->\manifest.json (這個可以放在網站的宣告)
        ->\test-install.ps1 (檢查執行安裝前的邏輯)
        ->\Windows10-next-steps.md
  ->\serviceWorker1
  1. 利用 Visual Studio 2017 開啓 source 目錄下的 App.jsproj,並綁定 Dev Center 中的 Project 來拿到 StoreKey.pfx 與相關的參數
  2. 把 manifest 目錄下的 appxmanifest.xml 中被標記為 INSERT-YOUR-PACKAGE-IDENTITY-NAME-HERE 的值改爲正確的值,包括:PublisherDisplayName,Identity 中的 Name, Publisher, Version
  3. 如果您想要更新 App 的 icons,可以利用 App Image Generator 選建立 Windows 10 的圖示,並把它們放到 manifest 與 source 目錄中的 images 目錄裏,並把 resources.pri 讓封裝的時候可以重新建立資源檔
  4. 安裝 node.js runtimenpm install pwabuilder -g,可以參考 Quick Start PWA using CLI tools
  5. 利用 PWA CLI tools 下指令來封裝 app:
    pwabuilder package {app name}-windows10\projects -p windows10 -l debug
    產生的 windows.appx 會放在 package 目錄,這裏不加 -a 的參數到指令裏面,因爲還不需要自動上傳到 Store
  6. StoreKey.pfx 加入 windows.appx 的憑證,加入憑證之後就可以安裝到 Windows 10 設備:
    cd "C:\Program Files (x86)\Windows Kits\10\bin\x86\"
    
    SignTool sign /fd SHA256 /a /f "C:\{app name}-windows10\projects\PWA\Store packages\windows10\package\StoreKey.pfx" "C:\{app name}-windows10\projects\PWA\Store packages\windows10\package\windows.appx"

以上就是利用 PWABuilder 建立可以安裝在 Windows 10 設備的介紹。

下面多補充説明如果把 PWABuilder 建立好的 appx 安裝在 XBOX 上面,網站需要加入那些調整。

把 PWA App 放在 Xbox 上執行(link)

UWP - 開發 Xbox App 處理 TV-safeUWP - 開發 Xbox App 處理 XY navigation 介紹過在 Xbox 上開發 App 要處理:

  • 如何停用滑鼠模式
    1. 下載 directionalnavigation-1.0.0.0.js 加到網頁裏面: <script src="directionalnavigation-1.0.0.0.js"></script>
    2. 在 javascript 中加入 navigator.gamepadInputEmulation = "gamepad";gamepadInputEmulation 有 3 種模式:預設是 mouse (啓用滑鼠模式);keyboard (停用滑鼠模式,切換用 keyboard 輸入的 DOM 鍵盤事件);gamepad (關閉滑鼠模式,不會產生 DOM 鍵盤事件,改使用 DOM 或 WinRT gamepad APIs)
  • 如何關閉縮放比例
    XAML App 預設調整為 200%, HTML App 則爲 150%。可以關閉並改用裝置的實際像素尺寸 (1910 x 1080 像素)。 JavaScript 的指令:
    var result = Windows.UI.ViewManagement.ApplicationViewScaling.trySetDisableLayoutScaling(true);
    或是 CSS:
    @media (max-height: 1080px) {   
        @-ms-viewport {   
            height: 1080px;   
        }   
    }   
  • 如何在螢幕邊緣繪製 UI
    預設 App 會針對電視保留安全區域(TV-safe area),確保内容能夠正常顯示。建議可以關閉,JavaScript 的指令:
    Windows.UI.ViewManagement.ApplicationView.getForCurrentView().setDesiredBoundsMode(Windows.UI.ViewManagement.ApplicationViewBoundsMode.useCoreWindow);
  • Javascript 處理 XY navigation 可以參考 Using DirectionalNavigation

======

Progress Web Apps(PWAs) 確實方便,但並不是全部 App 都能這樣取代,需要看 App 本身需求與硬體相依程度,不過大部分 80% 都能使用。

尤其是 Service Worker 的邏輯需要特別注意。

另外需要注意開發時,如果需要操作各個平臺的 APIs 只能經由 JavaScript 來互動,我建議可以多補充這方便的内容,有助於判斷那些功能可否實現。

希望對大家有所幫助,謝謝。

References: