在前几篇文章中,咱们使用service worker一步步优化了咱们的页面,如今咱们学习使用咱们以前的indexedDB, 来缓存咱们的ajax请求,第一次访问页面的时候,咱们请求ajax,当咱们继续刷新页面的时候,咱们从缓存里面去读取该json数据,想要了解indexedDB,请看这篇文章。
咱们下面的demo项目是创建在咱们第三篇文章的基础之上再进行的,想了解以前的文章,请点击这里.css
咱们仍是按照咱们以前的思路来作,首先咱们先看看咱们整个项目的架构以下:html
|----- 项目 | |--- 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(假如后端接口返回的数据是以下数据) 代码以下:node
{ "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 中,好比以下所示:jquery
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 代码以下:webpack
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(可读可写),如代码:ios
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) 来建立事务,而后建立事务完成后,而后咱们使用git
targetValue.transaction(storeName, transactionMode).objectStore(storeName);
这个方法,拿到 IDBObjectStore对象。而后把该对象 传入 successCallback 函数内部,在该回调函数中,咱们可使用 objectStore 来增长数据。github
getStore(): 该函数接收一个successCallback参数,指回调函数,在代码内部,咱们首先会建立一个事务,以下代码:web
var db = openObjectStore("store", function(objectStore) { }
而后咱们就会建立流标,而且对全部数据进行迭代,且监听onsuccess函数,以下代码:ajax
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修改数据的时候,咱们也同样能够修改咱们的某一条数据的。