[译]介绍一下渐进式 Web App(即时加载) - Part 2

代码紫霄使
• 阅读 4073

在上一篇,介绍一下渐进式 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
点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
6个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Jacquelyn38 Jacquelyn38
4年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Wesley13 Wesley13
3年前
Java日期时间API系列36
  十二时辰,古代劳动人民把一昼夜划分成十二个时段,每一个时段叫一个时辰。二十四小时和十二时辰对照表:时辰时间24时制子时深夜11:00凌晨01:0023:0001:00丑时上午01:00上午03:0001:0003:00寅时上午03:00上午0
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这