PWA 開發總結(坑多)

Sleepy ?發表於2019-04-23

Linux開發環境,Chrome除錯

Github連結weatherPWA

weatherPWA demo:

1

2

3

  1. 伺服器使用Chrome的內建app Web Server,更改埠即可使用(也可以自己搭建koa伺服器)
  2. 使用linux的chrome瀏覽器在使用sync功能的ServiceWoker監聽器時會抽風,windows親測無此類問題,所以儘量使用window開發PWA程式!!
  3. 使用push Notification功能時要用到公鑰私鑰,可用Postman進行推送測試
var vapidKeys = {
    publicKey:"BPwgIYTh9n2u8wpAf-_VzZ4dwaBY8UwfRjWZzcoX6RN7y5xD0RL9U4YDCdeoO3T8nJcWsQdvNirT11xJwPljAyk",
    privateKey:"TIrMnK-r--TE7Tnwf-x4JfKwuFKz5tmQuDRWYmuwbhY"
}
function subscribeUserToPush(registration , publicKey){
    var subscribeOptions = {
        userVisibleOnly : true,
        applicationServerKey : window.urlBase64ToUint8Array(publicKey)
    };
    return registration.pushManager.subscribe(subscribeOptions).then(function(pushSubscription){
        console.log('pushscription' ,pushSubscription)
        return pushSubscription;
    })
}
// base64 => Unit8Array
// https://github.com/web-push-libs/web-push#using-vapid-key-for-applicationserverkey
window.urlBase64ToUint8Array = function (base64String) {
    const padding = '='.repeat((4 - base64String.length % 4) % 4);
    const base64 = (base64String + padding)
      .replace(/\-/g, '+')
      .replace(/_/g, '/');
  
    const rawData = window.atob(base64);
    const outputArray = new Uint8Array(rawData.length);
  
    for (let i = 0; i < rawData.length; ++i) {
      outputArray[i] = rawData.charCodeAt(i);
    }
    return outputArray;
}

function sendSubscriptionToServer(body, url) {
    url = url || 'http://192.168.1.236:3000/subscription';
    return new Promise(function (resolve, reject) {
        var xhr = new XMLHttpRequest();
        xhr.timeout = 60000;
        xhr.onreadystatechange = function () {
            var response = {};
            if (xhr.readyState === 4 && xhr.status === 200) {
                try {
                    response = JSON.parse(xhr.responseText);
                    console.log("user subscribed!");
                    alert("subscribed!");
                    sub_state = 1;
                    window.localStorage.setItem("subscription",1);
                    $('#subscription_btn').remove();
                    $('#add_btn').remove();
                }
                catch (e) {
                    response = xhr.responseText;
                }
                resolve(response);
            }
            else if (xhr.readyState === 4) {
                resolve();
            }
        };
        xhr.onabort = reject;
        xhr.onerror = reject;
        xhr.ontimeout = reject;
        xhr.open('POST', url, true);
        xhr.setRequestHeader('Content-Type', 'application/json');
        xhr.send(body);
    });
}


function PUSH() {
    if (sub_state === 0) {
        if ('serviceWorker' in navigator && 'PushManager' in window) {
            var publicKey = "BPwgIYTh9n2u8wpAf-_VzZ4dwaBY8UwfRjWZzcoX6RN7y5xD0RL9U4YDCdeoO3T8nJcWsQdvNirT11xJwPljAyk";
            window.navigator.serviceWorker.ready.then(function (registration) {
                //displayNotification();
                // open the subscription function of the page
                return subscribeUserToPush(registration, publicKey);
            }).then(function (subscription) {
                
                var body = {subscription: subscription};
                // give every user a unique id in order to push notification
                body.uniqueid = new Date().getTime();
                console.log('uniqueid', body.uniqueid);
                console.log(JSON.stringify(body))
                // save the subscription info in the server (bedb used for saving)
                return sendSubscriptionToServer(JSON.stringify(body));
            }).then(function (res) {
                console.log(res);
            }).catch(function (err) {
                console.log(err);
            });
        }else{
            console.log('Push messaging is not supported.')
        }
        
    }else{
        alert("you have already subscribed.")
    }   
    
}
複製程式碼
  1. ServiceWorker內部無法使用Localstorage進行資料儲存,因為ServiceWorker是非同步儲存,所以如果需要將資料從ServiceWorker傳遞到前端,需要使用IndexedDB,建立新objectStore來進行資料通訊。在使用indexedDB是需要注意更新的版本號,如果版本號不變,則無法新增新的store,也就無法給資料庫新增新專案,只能更新已有資料。
  2. 在使用手機進行網頁測試時,對於後端伺服器的ip地址設定問題,需要格外注意。因為在手機chrome上除錯網頁時,需要使用PortForwarding來將localhost的網頁推到公網ip上測試,所以在後端的push埠和sync埠的ip地址就是當下手機和PC共有網路的ip地址,並不在localhost。也就是說在ServiceWorker中對於埠ip地址的呼叫,必須將ip地址更改,才能正常使用後端埠。
  3. 在ServiceWorker中無法使用ajax網路請求,需要使用fetch()方法來進行網路請求
fetch('http://192.168.1.137:3000/sync').then(function (response) {
      if (response.status !== 200) {
        console.log('Looks like there was a problem. Status Code: '+response.status);
        return;
      }
      response.text().then(function (data) {
        console.log("succeed access to sync interface");
        var request = indexedDB.open("weatherPWA");
        request.onupgradeneeded = function (event) {
          var store = event.target.result.createObjectStore("real time", {keyPath:'id',autoIncrement: true });  
          store.createIndex('time','time',{unique:false});
        }
        request.onsuccess = function (event) {
          console.log(data);
          db = this.result;
          var tx = db.transaction("real time",'readwrite');
          store = tx.objectStore("real time");
          var obj = {
            id:0,
            time:data
          }
          var req = store.put(obj);
          req.onsuccess = function (event) {     
              //console.log(obj+" insert in DB successful");
          };
          req.onerror = function (event) {
              console.log(obj+" insert in DB error",this.error);
          }
        }
        request.onerror = function (event) {
          console.log("opendb:", event);
        };
        console.log(data);
      })
    })
複製程式碼

相關文章