[譯]介紹一下漸進式 Web App(即時載入) - Part 2

naice發表於2017-12-15

在上一篇,介紹一下漸進式 Web App(離線) - Part 1的文章中,我們討論了典型的pwa應該是什麼樣子的並且同時也介紹了 server worker。到目前為止,我們已經快取了應用殼。在 index.htmllatet.html頁面中,我們的應用已經實現了離線載入快取資料。在重複訪問時,它們的載入速度更快。在本教程第一部分的結尾,我們能夠離線載入latest.html,但在使用者離線時無法顯示獲得動態資料。這次學習我們將:

  • 當使用者離線時候顯示在latest頁面快取 app的資料
  • 利用localStorage去儲存 app 的資料
  • 當使用者連線到Internet時,替換 app 舊的資料並獲取更新新的資料。

離線儲存

在構建PWA時,需要考慮各種儲存機制:

  • IndexedDB:這是一個事務型資料庫系統,用於客戶端儲存資料。IndexedDB允許您儲存和檢索用鍵索引的物件,以便對儲存在其中的資料進行高效能搜尋。IndexedDB暴露了一個非同步API,以避免阻塞DOM的載入。但一些研究表明,在某些情況下,它是阻塞的。使用IndexedDB時我推薦使用一些第三方庫,因為在JavaScript中操縱它可能非常冗長複雜。例如:localForageidbidb-keyval這些第三方模組都是很好滴。

[譯]介紹一下漸進式 Web App(即時載入) - Part 2

indexDB在瀏覽器的相容性

  • Cache API:這是儲存URL地址資源的最佳選擇。和Service worker配合是非常好滴。

  • PouchDB:是CouchDB的開源JavaScript資料庫。它使應用程式能夠在本地儲存資料,離線,然後同步與CouchDB和相容的伺服器應用程式時重新上線,保持使用者資料同步,不管他們下一次在哪裡登入。PouchDB支援所有現代瀏覽器,使用IndexedDB引擎失敗的話就降級到WebSQL,對Firefox 29+ (包括 Firefox OS and Firefox for Android), Chrome 30+, Safari 5+, Internet Explorer 10+, Opera 21+, Android 4.0+, iOS 7.1+ 和 Windows Phone 8+等等都是相容的。

  • Web Storage 例如 localStorage:它是同步的,是阻止DOM載入的,在瀏覽器中最多使用5MB, 它有簡單的 api去操作儲存鍵值對資料。

[譯]介紹一下漸進式 Web App(即時載入) - Part 2

Web Storage 的瀏覽器相容表

  • WebSQL:這是瀏覽器的關係型資料庫解決方案。它是已經被廢棄,因此,瀏覽器將來可能不支援它。

根據PouchDB的維護者 Nolan Lawson說,在使用資料庫時,最好問自己這些問題:

  • 這個資料庫是在記憶體中還是在磁碟上?(PouchDB, IndexedDB)?
  • 什麼需要儲存在磁碟上?應用程式關閉或崩潰時應該儲存哪些資料?
  • 需要什麼索引才能執行快速查詢?我可以使用記憶體索引而不是磁碟的嗎?
  • 我應該怎樣構造我的記憶體資料相對於我的資料庫資料?我在這兩者之間的對映策略是什麼?
  • 我的應用程式的查詢需求是什麼?展現檢視真的需要獲取完整的資料,還是隻需要獲取它所需要的一小部分呢?我可以延遲載入任何東西嗎?

您可以檢視考慮如何選擇資料庫,以便更全面地瞭解主題內容。

廢話少扯,讓我們實現即時載入

在我們的 web app 中,我們將用localStorage,由於我在本教程前面強調的侷限性,我建議你不要在生產環境中使用localStorage。我們正在構建的應用程式非常簡單,所以是使用了localStorage

開啟你的js/latest.js檔案,我們更新fetchCommits方法去儲存從Github API拉取的資料,儲存在localStorage。程式碼如下:


 function fetchCommits() {
    var url = 'https://api.github.com/repos/unicodeveloper/resources-i-like/commits';

    fetch(url)
    .then(function(fetchResponse){ 
      return fetchResponse.json();
    })
    .then(function(response) {
        console.log("Response from Github", response);

        var commitData = {};

        for (var i = 0; i < posData.length; i++) {
          commitData[posData[i]] = {
            message: response[i].commit.message,
            author: response[i].commit.author.name,
            time: response[i].commit.author.date,
            link: response[i].html_url
          };
        }

        localStorage.setItem('commitData', JSON.stringify(commitData));

        for (var i = 0; i < commitContainer.length; i++) {

          container.querySelector("" + commitContainer[i]).innerHTML = 
          "<h4> Message: " + response[i].commit.message + "</h4>" +
          "<h4> Author: " + response[i].commit.author.name + "</h4>" +
          "<h4> Time committed: " + (new Date(response[i].commit.author.date)).toUTCString() +  "</h4>" +
          "<h4>" + "<a href='" + response[i].html_url + "'>Click me to see more!</a>"  + "</h4>";

        }

        app.spinner.setAttribute('hidden', true); // hide spinner
    })
    .catch(function (error) {
      console.error(error);
    });
};
複製程式碼

上面有這段程式碼,在第一頁載入的時候,這些提交的資料就儲存到localStorage了,現在我們寫另外一個函式去渲染這些localStorage的資料。程式碼如下:


  // Get the commits Data from the Web Storage
  function fetchCommitsFromLocalStorage(data) {
    var localData = JSON.parse(data);

    app.spinner.setAttribute('hidden', true); //hide spinner

    for (var i = 0; i < commitContainer.length; i++) {

      container.querySelector("" + commitContainer[i]).innerHTML = 
      "<h4> Message: " + localData[posData[i]].message + "</h4>" +
      "<h4> Author: " + localData[posData[i]].author + "</h4>" +
      "<h4> Time committed: " + (new Date(localData[posData[i]].time)).toUTCString() +  "</h4>" +
      "<h4>" + "<a href='" + localData[posData[i]].link + "'>Click me to see more!</a>"  + "</h4>";

    }
  };
複製程式碼

這段程式碼將資料從本地儲存並將其渲染 dom 節點。

現在我們需要知道,什麼條件去呼叫fetchCommits函式和fetchCommitsFromLocalStorage函式。

js/latest.js程式碼如下


(function() {
  'use strict';

  var app = {
    spinner: document.querySelector('.loader')
  };

  var container = document.querySelector('.container');
  var commitContainer = ['.first', '.second', '.third', '.fourth', '.fifth'];
  var posData = ['first', 'second', 'third', 'fourth', 'fifth'];

  // Check that localStorage is both supported and available
  function storageAvailable(type) {
    try {
      var storage = window[type],
        x = '__storage_test__';
      storage.setItem(x, x);
      storage.removeItem(x);
      return true;
    }
    catch(e) {
      return false;
    }
  }

  // Get Commit Data from Github API
  function fetchCommits() {
    var url = 'https://api.github.com/repos/unicodeveloper/resources-i-like/commits';

    fetch(url)
    .then(function(fetchResponse){ 
      return fetchResponse.json();
    })
    .then(function(response) {
        console.log("Response from Github", response);

        var commitData = {};

        for (var i = 0; i < posData.length; i++) {
          commitData[posData[i]] = {
            message: response[i].commit.message,
            author: response[i].commit.author.name,
            time: response[i].commit.author.date,
            link: response[i].html_url
          };
        }

        localStorage.setItem('commitData', JSON.stringify(commitData));

        for (var i = 0; i < commitContainer.length; i++) {

          container.querySelector("" + commitContainer[i]).innerHTML = 
          "<h4> Message: " + response[i].commit.message + "</h4>" +
          "<h4> Author: " + response[i].commit.author.name + "</h4>" +
          "<h4> Time committed: " + (new Date(response[i].commit.author.date)).toUTCString() +  "</h4>" +
          "<h4>" + "<a href='" + response[i].html_url + "'>Click me to see more!</a>"  + "</h4>";

        }

        app.spinner.setAttribute('hidden', true); // hide spinner
      })
      .catch(function (error) {
        console.error(error);
      });
  };

  // Get the commits Data from the Web Storage
  function fetchCommitsFromLocalStorage(data) {
    var localData = JSON.parse(data);

    app.spinner.setAttribute('hidden', true); //hide spinner

    for (var i = 0; i < commitContainer.length; i++) {

      container.querySelector("" + commitContainer[i]).innerHTML = 
      "<h4> Message: " + localData[posData[i]].message + "</h4>" +
      "<h4> Author: " + localData[posData[i]].author + "</h4>" +
      "<h4> Time committed: " + (new Date(localData[posData[i]].time)).toUTCString() +  "</h4>" +
      "<h4>" + "<a href='" + localData[posData[i]].link + "'>Click me to see more!</a>"  + "</h4>";

    }
  };

  if (storageAvailable('localStorage')) {
    if (localStorage.getItem('commitData') === null) {
      /* The user is using the app for the first time, or the user has not
       * saved any commit data, so show the user some fake data.
       */
      fetchCommits();
      console.log("Fetch from API");
    } else {
      fetchCommitsFromLocalStorage(localStorage.getItem('commitData'));
      console.log("Fetch from Local Storage");
    }   
  }
  else {
    toast("We can't cache your app data yet..");
  }
})();

複製程式碼

在上面的程式碼片斷,我們正在檢查瀏覽器是否支援本地儲存,如果它支援,我們繼續檢查是否已經快取了提交資料。如果沒有被快取,我們將請求資料,顯示到頁面上並且快取請求的資料。

現在,從新重新整理一遍瀏覽器,確保你做了一個清晰的快取,強制重新整理,否則我們不會看到我們的程式碼更改的結果。

現在,離線並載入最新頁面。將發生了什麼事呢?

Yaaay!!! 它載入資料沒有任何問題。

[譯]介紹一下漸進式 Web App(即時載入) - Part 2

檢視DevTools,你間看到資料已經被快取到localStorage

[譯]介紹一下漸進式 Web App(即時載入) - Part 2

當使用者離線時,看看它載入的速度!!!

[譯]介紹一下漸進式 Web App(即時載入) - Part 2

還有一件事

現在,我們可以立即從本地儲存獲取資料。但是我們如何獲得最新的資料?當使用者線上時,我們需要一種仍然獲得新資料的方法。

so easy, 讓我們新增一個重新整理按鈕,觸發一個請求到GitHub獲得的最新資料。

開啟latest.html檔案,並且新增一個重新整理按鈕到<header>標籤

<button id="butRefresh" class="headerButton" aria-label="Refresh"></button>
複製程式碼

新增的按鈕後<header>標籤應該是這樣的:


<header>
  <span class="header__icon">
    <svg class="menu__icon no--select" width="24px" height="24px" viewBox="0 0 48 48" fill="#fff">
      <path d="M6 36h36v-4H6v4zm0-10h36v-4H6v4zm0-14v4h36v-4H6z"></path>
    </svg>
  </span>
  <span class="header__title no--select">PWA - Commits</span>
  <button id="butRefresh" class="headerButton" aria-label="Refresh"></button>
</header> 
複製程式碼

最後,讓我們在按鈕上附加一個單擊事件並新增功能。開啟js/latest.js並且新增如下程式碼:

document.getElementById('butRefresh').addEventListener('click', function() {
    // Get fresh, updated data from GitHub whenever you are clicked
    toast('Fetching latest data...');
    fetchCommits();
    console.log("Getting fresh data!!!");
});
複製程式碼

清除快取並重新載入。現在,你的latest.html頁面看起來應該像這樣:

[譯]介紹一下漸進式 Web App(即時載入) - Part 2

每當使用者需要最新資料時,他們只需單擊重新整理按鈕即可。

附加:

點選檢視下面連結

上一篇: [譯]介紹一下漸進式 Web App(離線) - Part 1

原文地址

專案程式碼地址

個人部落格地址

如果有那個地方翻譯出錯或者失誤,請各位大神不吝賜教,小弟感激不盡

期待下一篇: 介紹一下漸進式 Web App(訊息推送) - Part 3

相關文章