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

在上一篇,介紹一下漸進式 Web App(離線) - Part 1的文章中,咱們討論了典型的pwa應該是什麼樣子的而且同時也介紹了 server worker。到目前爲止,咱們已經緩存了應用殼。在 index.htmllatet.html頁面中,咱們的應用已經實現了離線加載緩存數據。在重複訪問時,它們的加載速度更快。在本教程第一部分的結尾,咱們可以離線加載latest.html,但在用戶離線時沒法顯示得到動態數據。此次學習咱們將:html

  • 當用戶離線時候顯示在latest頁面緩存 app的數據
  • 利用localStorage去存儲 app 的數據
  • 當用戶鏈接到Internet時,替換 app 舊的數據並獲取更新新的數據。

離線存儲

在構建PWA時,須要考慮各類存儲機制:git

  • IndexedDB:這是一個事務型數據庫系統,用於客戶端存儲數據。IndexedDB容許您存儲和檢索用鍵索引的對象,以便對存儲在其中的數據進行高性能搜索。IndexedDB暴露了一個異步API,以免阻塞DOM的加載。但一些研究代表,在某些狀況下,它是阻塞的。使用IndexedDB時我推薦使用一些第三方庫,由於在JavaScript中操縱它可能很是冗長複雜。例如:localForageidbidb-keyval這些第三方模塊都是很好滴。

indexDB在瀏覽器的兼容性github

  • 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 Storage 的瀏覽器兼容表web

  • WebSQL:這是瀏覽器的關係型數據庫解決方案。它是已經被廢棄,所以,瀏覽器未來可能不支持它。

根據PouchDB的維護者 Nolan Lawson說,在使用數據庫時,最好問本身這些問題:數據庫

  • 這個數據庫是在內存中仍是在磁盤上?(PouchDB, IndexedDB)?
  • 什麼須要存儲在磁盤上?應用程序關閉或崩潰時應該保存哪些數據?
  • 須要什麼索引才能執行快速查詢?我可使用內存索引而不是磁盤的嗎?
  • 我應該怎樣構造個人內存數據相對於個人數據庫數據?我在這二者之間的映射策略是什麼?
  • 個人應用程序的查詢需求是什麼?展示視圖真的須要獲取完整的數據,仍是隻須要獲取它所須要的一小部分呢?我能夠延遲加載任何東西嗎?

您能夠查看考慮如何選擇數據庫,以便更全面地瞭解主題內容。apache

廢話少扯,讓咱們實現即時加載

在咱們的 web app 中,咱們將用localStorage,因爲我在本教程前面強調的侷限性,我建議你不要在生產環境中使用localStorage。咱們正在構建的應用程序很是簡單,因此是使用了localStoragenpm

打開你的js/latest.js文件,咱們更新fetchCommits方法去存儲從 Github API 拉取的數據,存儲在localStorage。代碼以下:json

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的數據。代碼以下:segmentfault

// 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 節點。api

如今咱們須要知道,什麼條件去調用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!!! 它加載數據沒有任何問題。

查看DevTools,你間看到數據已經被緩存到localStorage

當用戶離線時,看看它加載的速度!!!

還有一件事

如今,咱們能夠當即從本地存儲獲取數據。可是咱們如何得到最新的數據?當用戶在線時,咱們須要一種仍然得到新數據的方法。

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 1

原文地址

項目代碼地址

我的博客地址

若是有那個地方翻譯出錯或者失誤,請各位大神不吝賜教,小弟感激涕零

期待下一篇: 介紹一下漸進式 Web App(消息推送) - Part 3
相關文章
相關標籤/搜索