在前几篇文章中,我们使用service worker一步步优化了我们的页面,现在我们学习使用我们之前的indexedDB, 来缓存我们的ajax请求,第一次访问页面的时候,我们请求ajax,当我们继续刷新页面的时候,我们从缓存里面去读取该json数据,想要了解indexedDB,请看这篇文章
我们下面的demo项目是建立在我们第三篇文章的基础之上再进行的,想了解之前的文章,请点击这里.

我们还是按照我们之前的思路来做,首先我们先看看我们整个项目的架构如下:

|----- 项目
|  |--- public
|  | |--- js               # 存放所有的js
|  | | |--- main.js        # js入口文件
|  | | |--- store.js
|  | | |--- myAccount.js
|  | |--- style            # 存放所有的css
|  | | |--- main.styl      # css 入口文件
|  | |--- json             # 存放本地模拟数据的json文件
|  | | |--- index.json
|  | |--- index.html       # index.html 页面
|  | |--- images
|  |--- package.json
|  |--- webpack.config.js
|  |--- node_modules
|  |--- sw.js

public/js/index.json(假如后端接口返回的数据是如下数据) 代码如下:

{
  "code": 0,
  "data": [
    { "name": "kongzhi111", "age": 28},
    { "name": "kongzhi222", "age": 29},
    { "name": "kongzhi333", "age": 30}
  ]
}

在我们的 public/js 下新建一个 store.js 文件,该js文件的作用是使用indexedDB来缓存我们的ajax请求数据的。并且我们需要把该 store.js 文件存放在我们的 sw.js 中的 CACHE_URLS 中,比如如下所示:

var CACHE_URLS = [
  "/public/index.html",      // html文件
  "/main.css",               // css 样式表
  "/public/images/xxx.jpg",  // 图片
  "/main.js",                 // js 文件
  "/public/js/store.js"
];

然后我们开始编写我们的 store.js 代码,/public/js/store.js 代码如下:

import axios from 'axios';

var openDataBase = function() {
  if (!window.indexedDB) {
    return false;
  }
  // 打开或创建 store-data 数据库
  var result = window.indexedDB.open('store-data', 2);

  // 监听error函数触发
  result.onerror = function(event) {
    console.log("DataBase error:", event.target.error);
  }
  // 监听当前版本号被升级的时候触发该函数
  result.onupgradeneeded = function(event) {
    var db = event.target.result;
    /*
     是否包含该对象仓库名(或叫表名)。如果不包含就创建一个。
     该对象中的 keyPath属性id为主键
    */
    if (!db.objectStoreNames.contains('store')) {
      db.createObjectStore("store", { keyPath: "id", autoIncrement: true });
    }
  }
  return result;
};
/*
 @param {storeName} 仓库名或表名
 @param {successCallback} 需要执行的回调函数
 @param {transactionMode} 事务模式 readOnly 只读,readwrite 可读可写
*/
var openObjectStore = function(storeName, successCallback, transactionMode) {
  var db = openDataBase();
  if (!db) {
    return false;
  }
  db.onsuccess = function(event) {
    var targetValue = event.target.result;
    /*
     1. 使用 targetValue.transaction(storeName, transactionMode) 来创建事务
     2. 创建事务之后,我们使用 targetValue.transaction(storeName, transactionMode).objectStore(storeName)
     这个方法,拿到 IDBObjectStore对象。
    */
    var objectStore = targetValue.transaction(storeName, transactionMode).objectStore(storeName);
    successCallback(objectStore);
  };
  return true;
};

var getStore = function (successCallback) {
  var datas = [];
  var db = openObjectStore("store", function(objectStore) {
    // 使用流标 objectStore.openCursor()
    objectStore.openCursor().onsuccess = function(event) {
      var cursor = event.target.result;
      // 如果有流标的话,就把数据放入数组datas里面去,依次循环存入进去
      if (cursor) {
        datas.push(cursor.value);
        cursor.continue();
      } else {
        // 否则的话,如果datas有数据的话,就支持调用回调函数
        if (datas.length > 0) {
          successCallback(datas);
        } else {
          // 如果datas数据为空,发送一个json请求
          axios.get("http://localhost:8081/public/json/index.json").then(datas => {
            var list = datas.data.data;
            // 打开数据仓库或表名,执行对应的事务操作
            openObjectStore("store", function(datasStore) {
              for (let i = 0; i < list.length; i++) {
                datasStore.add(list[i]);
              }
              successCallback(datas);
            }, "readwrite");
          });
        }
      }
    }
  });
  if (!db) {
    axios.get("http://localhost:8081/public/json/index.json", successCallback);
  }
};

window.getStore = getStore;

如上代码,有三个函数,分别为 openDataBase、openObjectStore、及 getStore, 那么第一个函数 openDataBase() 会打开一个新的数据库请求,该函数代码内部,首先会判断浏览器是否支持 window.indexedDB ,如果不支持的话,直接返回,然后接着我们创建了一个 store-data 数据库,并且监听了 onerror, onupgradeneeded 事件,最后我们创建了一个 store 仓库名或叫表名。并且以id作为主键,并且设置了 autoIncrement 为true,自动增长。然后我们返回了该 result。因为我们的 onsuccess事件并没有在该方法中监听。在第二个函数 openObjectStore 中,我们会调用 创建数据库的这个函数,并且去监听 onsuccess这个事件。
openObjectStore() 该函数会在对象上打开一个事务,并且在其运行函数,该方法中的第一个参数为 仓库名称,第二个参数为打开仓库后成功的回调函数,第三个参数是可选参数,它的含义是事务的类型,有 readonly(只读) 或 readwrite(可读可写),如代码:

db.onsuccess = function(event) {
  var targetValue = event.target.result;
  /*
   1. 使用 targetValue.transaction(storeName, transactionMode) 来创建事务
   2. 创建事务之后,我们使用 targetValue.transaction(storeName, transactionMode).objectStore(storeName)
   这个方法,拿到 IDBObjectStore对象。
  */
  var objectStore = targetValue.transaction(storeName, transactionMode).objectStore(storeName);
  successCallback(objectStore);
};

首先我们使用该代码:使用 targetValue.transaction(storeName, transactionMode) 来创建事务,然后创建事务完成后,然后我们使用

targetValue.transaction(storeName, transactionMode).objectStore(storeName);
这个方法,拿到 IDBObjectStore对象。然后把该对象 传入 successCallback 函数内部,在该回调函数中,我们可以使用 objectStore 来增加数据。

getStore(): 该函数接收一个successCallback参数,指回调函数,在代码内部,我们首先会创建一个事务,如下代码:

var db = openObjectStore("store", function(objectStore) {

}

然后我们就会创建流标,并且对所有数据进行迭代,且监听onsuccess函数,如下代码:

var db = openObjectStore("store", function(objectStore) {
  // 使用流标 objectStore.openCursor()
  objectStore.openCursor().onsuccess = function(event) {
    var cursor = event.target.result;
    // 如果有流标的话,就把数据放入数组datas里面去,依次循环存入进去
    if (cursor) {
      datas.push(cursor.value);
      cursor.continue();
    } else {
      // 否则的话,如果datas有数据的话,就支持调用回调函数
      if (datas.length > 0) {
        successCallback(datas);
      } else {
        // 如果datas数据为空,发送一个json请求
        axios.get("http://localhost:8081/public/json/index.json").then(datas => {
          var list = datas.data.data;
          // 打开数据仓库或表名,执行对应的事务操作
          openObjectStore("store", function(datasStore) {
            for (let i = 0; i < list.length; i++) {
              datasStore.add(list[i]);
            }
            successCallback(datas);
          }, "readwrite");
        });
      }
    }
  }
}

如上代码,如果有流标的话,在流标每次前进到一个新的记录时都会被调用,甚至在流标通过最后一条记录之后也会被调用,它是通过 continue 来对内部进行循环调用,当到最后一条记录的时候,它后面就没有数据了,因此就会进入 else 语句内部。因此首先会判断datas 是否有数据,如果有数据的话,就会调用 successCallback(datas); 这句代码,把数据datas作为参数传回给 successCallback 回调函数,否则的话,如果datas为空的话,我们就会去请求我们本地的json请求,发送ajax请求,然后把请求的数据,存入到 store仓库名中,依次循环完成后,我们再调用 successCallback方法,把数据datas作为参数传递出去。当然在我们的 第三个函数 getStore函数中,如果不支持window.indexedDB的话,那么该浏览器的话,我们直接去请求ajax, 如getStore最后一句代码:

if (!db) {
  axios.get("http://localhost:8081/public/json/index.json", successCallback);
}

最后再我们的store.js 中,我们会使用 window.getStore = getStore; 让其成为全局的。然后在我们的 /public/js/myAccount.js 代码如下,就可以调用我们的 store.js 中的 getStore方法了,如下代码所示:

import $ from 'jquery';

$(function() {
  // 请求数据并且渲染数据
  requestAndRenderFunc();

  // 向服务器请求数据,并且渲染页面
  function requestAndRenderFunc () {
    getStore(renderHTMLFunc);
  };

  function renderHTMLFunc(datas) {
    console.log(datas);
  }
});

如上是所有优化后的代码,使用indexedDB来存储我们的数据。因此我们来测试下页面,我们首先清空下我们浏览器的缓存数据,然后我们第一次访问下我们的页面,我们可以看到我们的网络上,显示如下请求:

然后我们在控制台中会看到返回的数据,如下图所示:

如上我们第一次请求的时候,我们可以看到会请求ajax,然后会返回内容,现在我们继续刷新我们的页面,可以看到如下请求,如下所示:

然后再看我们的控制台打印如下所示:

我们可以看到我们ajax请求并没有发请求,但是我们依然可以拿到数据,这是为什么呢?这是因为我们使用 indexedDB缓存ajax数据到本地,因此当我们第二次以后请求的时候,我们拿的都是 indexedDB里面的数据,我们并没有发ajax请求,所以使用该访问,哪怕以后访问我们的页面,即使没有网络的情况下,我们依然可以拿到数据,并且更快加载我们的页面。我们再来看下我们的 indexedDB存储的数据如下所示:

如上代码我们已经实现了使用indexedDB对数据缓存了,并且使用 indexedDB缓存里面的数据了,但是现在有一个新的问题,并且用户点击一个查询按钮,但是查询按钮的条件发生改变了,因此ajax请求返回的数据也是根据页面中查询的条件来返回的,因此这个时候我们就不能一直使用 indexedDB中的数据了,我们需要重新请求页面的数据,因此我们需要在我们的 store.js 添加如下代码了:

var addToObjectStore = function(storeName, object) {
  openObjectStore(storeName, function(store) {
    store.add(object);
  }, "readwrite");
};

var updateInObjectStore = function(storeName, id, object) {
  openObjectStore(storeName, function(objectStore) {
    objectStore.openCursor().onsuccess = function(event) {
      var cursor = event.target.result;
      if (!cursor) {
        return;
      }
      if (cursor.value.id === id) {
        objectStore.put(object);
        return;
      }
      cursor.continue();
    }
  }, "readwrite");
}

window.addToObjectStore = addToObjectStore;
window.updateInObjectStore = updateInObjectStore;

如上 addToObjectStore 函数接收对象存储的名称以及要放进存储的新对象作为参数,该函数我们可以使用如下方式来进行调用:

addToObjectStore("store", { id: 1 });

第二个函数 updateInObjectStore 接收对象存储的名称,找到与给定的id参数匹配的id对象,并且用它来更新对象,这是通过在对象存储上打开 readwrite事务,并且使用流标进行迭代来完成的。在流标到达最后一条记录或匹配成功之前,函数会一直迭代。如果找到匹配项,就会通过 objectStore.put(object); 来进行更新,此时函数就会通过return返回回来,因为一旦找到匹配,就不需要继续迭代下一条记录了。该函数可以如下调用:

updateInObjectStore("store", 1, {"id": 1, name: 'kongzhi', age: 30 });

因此为了演示下,我们需要把我们的index.html 代码变成如下了:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>service worker 实列</title>
</head>
<body>
  <div id="app">222226666</div>
  <img src="/public/images/xxx.jpg" />
  <div style="cursor: pointer;color:red;font-size:18px;padding:5px;border:1px solid #333;" id="submit">点击我新增</div>

  <div style="cursor: pointer;color:red;font-size:18px;padding:5px;border:1px solid #333;" id="update">点击我修改</div>

</body>
</html>

如上代码,我们新增了 id = "submit" div元素,和 id = "update" 的元素,然后需要在我们的myAccount.js 代码添加如下:

function updateDisplay(d) {
  console.log(d);
};
function renderHTMLFunc(datas) {
  console.log(datas);
}
var addStore = function(id, name, age) {
  var obj = {
    id: id,
    name: name,
    age: age
  };
  addToObjectStore("store", obj);
  renderHTMLFunc(obj);
  $.getJSON("http://localhost:8081/public/json/index.json", obj, function(data) {
    updateDisplay(data);
  });
};
$("#submit").click(function(e) {
  addStore(3, 'longen1', '111');
});
$("#update").click(function(e) {
  $.getJSON("http://localhost:8081/public/json/index.json", {id: 1}, function(data) {
    updateInObjectStore("store", 1, data);
    updateDisplay(data);
  });
});

如上代码,当我们点击 id 为 submit的元素的时候,我们会调用 addStore 函数,在该函数内部会根据 id, name, age 参数来新增一条数据,然后会调用 addToObjectStore 函数,先把数据添加到本地存储里面去,然后在渲染页面 调用 renderHTMLFunc 函数,最后使用 $.getJSON 请求一条数据,然后把最新的数据渲染到页面上去。

同样的道理,update数据的时候,我们会发ajax请求,无论服务器端是否返回了新的数据,我们都会调用 updateInObjectStore 这个函数来更新我们本地的数据。这样就实现了,如果是状态发送改变的话,那么本地indexedDB存储的数据库也会重新得到更新。

我们可以在我们的项目点击下就可以看到效果了。我们点击新增一条数据后,在我们的 indexedDB中看到信息如下:

如上我们这边没有把 data里面的数据抽离出来,直接把一整个数据直接添加进去了,反正就是这个意思,新增的时候,重新能更新我们的indexedDB里面的数据,同理我们update修改数据的时候,我们也一样可以修改我们的某一条数据的。

github源码查看demo

渐进式web应用开发---使用indexedDB实现ajax本地数据存储(四)的更多相关文章

  1. 渐进式web应用开发---ajax本地数据存储(四)

    在前几篇文章中,我们使用service worker一步步优化了我们的页面,现在我们学习使用我们之前的indexedDB, 来缓存我们的ajax请求,第一次访问页面的时候,我们请求ajax,当我们继续 ...

  2. iOS开发 - OC - 实现本地数据存储的几种方式二(直接使用sqlite)

    连接上一篇文章http://www.cnblogs.com/FBiOSBlog/p/5819418.html. 上一篇文章介绍了OC内部一些方法进行数据的本地存储,其中包括 NSUser类.Plist ...

  3. iOS开发 - OC - 实现本地数据存储的几种方式一

    iOS常用的存储方式介绍 在iOS App开发过程中经常需要操作一些需要持续性保留的数据,比如用户对于App的相关设置.需要在本地缓存的数据等等.本文针对OC中经常使用的一下存储方式做了个整理. 常用 ...

  4. 渐进式web应用开发---promise式数据库(五)

    在前面的一篇文章中,我们已经实现了使用indexedDB实现ajax本地数据存储的功能,详情,请看这篇文章.现在我们需要把上面的一篇文章中的代码使用promise结构来重构下.我们为什么需要使用pro ...

  5. 渐进式web应用开发---service worker 原理及介绍(一)

    渐进式web应用(progressive Web app) 是现代web应用的一种新形式.它利用了最新的web功能,结合了原生移动应用的独特特性与web的优点,为用户带来了新的体验. 一:传统web端 ...

  6. iOS开发技术分享(1)— iOS本地数据存储

    iOS开发技术分享(1)— iOS本地数据存储 前言: 我本是一名asp.net程序员,后来加入了iOS游戏开发队伍,到现在也有一年多的时间了.这一年来,每天都干到2.3点钟才睡觉,不为别的,只为了学 ...

  7. Windows 8 应用开发 - 本地数据存储

    原文:Windows 8 应用开发 - 本地数据存储      在应用中通常会遇到用户主动或被动存储信息的情况,当应用关闭后这些数据仍然会存储在本地设备上,用户下次重新激活应用时会自动加载这些数据.下 ...

  8. 微信小程序开发:学习笔记[9]——本地数据缓存

    微信小程序开发:学习笔记[9]——本地数据缓存 快速开始 说明 本地数据缓存是小程序存储在当前设备上硬盘上的数据,本地数据缓存有非常多的用途,我们可以利用本地数据缓存来存储用户在小程序上产生的操作,在 ...

  9. 渐进式web应用开发-- 使用后台同步保证离线功能(六)

    _ 阅读目录 一:什么是后台同步保证离线功能呢? 二:后台同步是如何实现的呢? 三:如何给sync事件传递数据? 四:在我们的项目中添加后台同步功能 回到顶部 一:什么是后台同步保证离线功能呢? 在我 ...

随机推荐

  1. 基于Django框架 CRM的增删改查

    思路: 创建表------从数据库读出数据展示出来------配置路由-----写视图函数------写对应页面 练习点: 数据库建表 ORM 数据库数据读取 数据 ModelForm  (form组 ...

  2. 灵雀云CTO陈恺应邀出席国泰君安信息产业投资峰会,探讨全球科技产业新格局

    2019年7月9-10日,国泰君安信息产业投资峰会在上海陆家嘴举办.作为国内容器PaaS领域的龙头公司,灵雀云受邀出席本次大会,在“数字化转型从云做起”的论坛中,CTO陈恺发表了<云原生助力企业 ...

  3. centos 上安装redis 3.0.5

    官网下载安装包,直接使用make编译,报如下错误 : [root@localhost redis-3.0.5]# make cd src && make all make[1]: 进入 ...

  4. C语言指针专题——序

    看到好多的C语言初学者学到指针时,都觉得指针怎么那么难啊!我也想起了我当时学习指针时遇到的困难,确实很难!到底是教程写的不好呢,还是老师教的不好呢?我觉得都有. 网上搜索指针讲解的资料很多,我也看了不 ...

  5. 各类最短路算法基本模板-C++

    原文转自:https://blog.csdn.net/changjiale110/article/details/77394650 感谢. #define Max 0x3f3f3f3f #define ...

  6. Excel催化剂开源第10波-VSTO开发之用户配置数据与工作薄文件一同存储

    在传统的VBA开发中,若是用的是普通加载项方法,是可以存储数据在xlam上的,若用的是Com加载项方法同时是Addins程序级别的项目开发的,配置文件没法保存到工作薄中,一般另外用配置文件来存放供调用 ...

  7. Tiny Counting

    也许更好的阅读体验 样例一 输入 4 1 4 3 2 输出 3 样例二 输入 5 9 1 0 0 5 输出 8 题解 这是本人自己想了2个半小时才想出来的方法,稍稍有点复杂但是很好理解 题目的意思就是 ...

  8. C#2.0增功能04 可以为 null 的类型

    连载目录    [已更新最新开发文章,点击查看详细] 可以为 null 的类型是 System.Nullable<T> 结构的实例. 可以为 null 的类型可表示一个基础类型的所有值 T ...

  9. leetcode 198. House Robber (Easy)

    https://leetcode.com/problems/house-robber/ 题意: 一维数组,相加不相邻的数组,返回最大的结果. 思路: 一开始思路就是DP,用一维数组保存dp[i]保存如 ...

  10. 抽象数据类型与C++

    类是一种新的数据类型,类似于数据结构,只是它拥有数据结构所没有的部分——“成员函数”,正是因为它所拥有的成员函数这一特性,使得它能隐藏“数据结构”(类)中的数据,不被用户所知道.通过类中的成员函数,使 ...