cookie
使用客服端存储的一个好处是不通过网络发送数据,来回通过http header发送cookie信息恰好违背了这一点,所以一般情况下不建议使用cookie存储,但是如果你希望有一些值是服务器应该知道的,那么就可以使用cookie确保服务器看到。cookie存储限制一般为4KB。
一、查看http中的cookie信息
存在cookie的信息会在http请求的时候携带到服务器,可在request headers中查看
二、cookie的使用
1.设置cookie(cookie没有api,所以直接访问document.cookie对象)
//设置第一个cookie
document.cookie = "nameOfCookie=value";
//设置第二个cookie,expires为过期时间,domain可以设置对某个子域名有效(默认只对当前域名有效)
document.cookie = "name=Raymond;expires=Fri,31 Dec 9999 23:59:59 GMT;
domain=app.foo.com";
//动态定义cookie,需要使用类似encodeURIComponent的辅助函数,如
var name = "Raymond Camden";
document.cookie = "name=" + encodeURIComponent(name);
2.读取cookie
//读取出来的cookie包含所有信息,需要通过指定字符串分割获取
console.log(document.cookie)
3.删除cookie(只需要将过期时间设为过去时间即可)
document.cookie = "name=Raymond; expires=Thu, 01 Jan 1970 00:00:00 GMT";
web存储(localStorage和sessionStorage)
web存储有两个版本:本地存储(local storage)和会话存储(session storage)。两者是使用方式是完全一样的,区别在于:本地存储是持久存在的(或者用户主动删除),而会话存储是只要浏览器关闭就会消失。和cookie类似,web存储也是与域名一一对应的。和cookie不同的是,主域名无法和子域名共享存储的数据(可以借助iframe变通实现)。web存储限制一般为5-10MB。
一、使用方式
注意:web存储会将所有的数据都作为字符串存储,包括数字。如若存储引用数据类型,可以配合JSON.stringfy与JSON.parse使用
//存
sessionStorage.setItem("name", "张三");
//取
sessionStorage.getItem("name");
//删除
sessionStorage.removeItem(name);
//删除全部
sessionStorage.cleat();
/*
* 引用类型存储方式
*/
//存
sessionStorage.setItem("userInfo",JSON.stringify({"name":"张三","age":18});
//取
JSON.parse(sessionStorage.getItem("userInfo"));
二、存储事件
存储事件只有在浏览器的另外一个实例修改存储时,才会被触发(只要另外打开一个浏览器,输入相同的URL,在心打开的页签中修改存储的值,然后返回原来的页签,就能看到存储事件本身的信息)。不过,即使能监听到存储发生了变化,也不能再根据不同的页签存储不同的数据了,因为存储系统已经变了,这是不可逆的。唯一可以做的就是,监听存储事件获取最新的值并更新响应的页面数据。
1.代码示例
//监听sotrage变化
window.addEventListener('storage', function (e) {
console.log(e.key) //key
console.log(e.newValue) //新值
console.log(e.oldValue) //旧值
})
IndexedDB
indexedDB是和域名一一对应的,它对存储大小及存储的数据类型没有任何限制,可以将它看成是一个数据库的存在。
也可以使用Dexie.js第三方库,官网地址Dexie.js - Minimalistic IndexedDB Wrapper
一、检查浏览器是否支持indexedDB
// 检查是否支持并且判断当前设备是否为ipad或者iphone
function idbOK() {
return "indexedDB" in window && !/iPad|iPhone|iPod/.test(navigator.platform);;
}
二、使用IndexedDB
注意:indexedDB中所有的操作都是异步的。因此打开数据库并不代表立即就可以使用,而是需要通过一个响应事件之后才可以使用。
1.打开一个数据库
需要设置一个名称和版本号,版本号通常从1开始,数据库结构(指对象存储和索引,而不是数据本身)只能在更改版本时调整。
//打开数据库,设置数据库名称为ora_db1,版本号为1 var openRequest = indexedDB.open("ora_db1",1);
打开数据库可以触发的事件有:
- success //成功
- error //失败
- upgradeneeded //在用户首次访问数据库或版本号发生变化时触发
- blocked //数据库不可用时触发
你可以像下面这样考虑这个过程:
- 请求打开数据库;
- 如果请求触发了一个upgradeneeded事件,则创建对象存储;
- 如果请求触发了一个success事件,则表明数据库已经准备就绪。
2. 创建一个空的存储对象
注意:创建对象存储必须在upgradeneeded事件中完成。
2.1 创建对象存储并定义主键
使用createObjectStore()方法创建对象存储,支持传递两个参数
- name 对象名称
- options 定义对象存储的各种配置属性,包括索引(每条数据中的唯一值)
keyPath 包含唯一信息的属性且此值不能为空
autoIncrement 使用自增值自动为主键赋值,如果同时定义了keyPath则会将自增值存储在keyPath定义的属性中。var peopleOS=objectStore.createObjectStore("people", {keyPath: "id", autoIncrement:true});
2.2. 定义索引(非必须)
在指定了存储对象和主键后可以定义索引,它能定义你将如何从对象存储中获取数据。
使用createIndex()方法创建索引,支持传递三个参数
- name 索引名称
- path 希望索引的数据属性,大多数情况下,和第一个参数使用相同的值
- options 定义如何操作索引,只有两种:一种指定唯一性,另一种专门用于映射到数据的数据{multientry:true}
//指定索引为性别且不唯一 peopleOS.createIndex("sex", "sex", {unique:false}); //指定索引为证件号且唯一 peopleOS.createIndex("ssn", "ssn", {unique:true}); //指定索引为爱好 peopleOS.createIndex("hobbies", "hobbies", {unique:false,multiEntry:true});
3.操作数据(CRUD)
注意:
1.只有当onsuccess处理器运行,才可以开始操作数据。
2. indexedDB的所有数据操作都是在事物中完成的,如果一个事物中的某个操作出现问题,那么任何修改都会回滚。
3. 事物特定于一个或者多个对象存储(指你需要操作的存储对象),这些存储对象可以是只读的(用于获取数据),也可以为读写的(用于修改数据)。
4. CRUD之前都必须要创建事物,并定义需要操作的存储对象及事物类型(读写操作)。
3.1 创建数据
使用store.add()创建数据,支持两个参数
- data 数据(必填),可以为任意类型
- somekey 主键
添加数据也是异步的,因此,需要监听事件来检查添加状态。
function add(){
//创建事物并设置操作类型,readwrite:读写,readonly:只读
//db为数据库打开成功时的数据库对象,为全局变量,在onsuccess函数中进行赋值
var transaction = db.transaction(["people"],"readwrite");
//从事务请求对象存储。
var store = transaction.objectStore("people");
//数据
var person = {
id:1,
name:"张三",
ssn:"166575758332344",
sex:"男",
email:"56778899@qq.com"
hobbies:["电影","做饭","美食"],
}
//添加,将id设置为主键
var request = store.add(person);
//添加成功触发
request.onsuccess = function(){
}
//添加失败
request.onerror = function(){
}
}
3.2 读取数据
使用store.get(key)读取数据
读取数据也是异步的,key为设置的存储对象中的主键。
function getLogs(e) {
//创建事物,并制定存储对象和类型
var transaction = db.transaction(["people"],"readonly");
//请求存储对象
var store = transaction.objectStore("people");
//获取数据
var request = store.get(Number(key));
//获取数据成功
request.onsuccess = function(e) {
var result = e.target.result;
console.dir(result);
}
//获取数据失败
request.onerror = function(e) {
console.log("Error");
console.dir(e);
}
}
3.3 更新数据
使用store.put(data),依旧是异步。
使用方法和store.add一样,需要创建一个事物,然后使用事物返回的存储对象的变量,调用其put方法,也可以使用第二个参数指定主键。
3.4 删除数据
store.delete(key) key依旧为主键。
同样,该操作也要在事务中进行,而且也是异步的。
注意:即使删除的主键不存在,删除操作也会触发onsuccess事件处理器。如果想要避免这种情况的话,就需要在删除之前先判断一下要删除的数据是否存在。
4. 获取所有数据
IndexedDB使用一个名为游标(cursor)的东西遍历存储对象中的数据。可以将游标想象成一只快乐的小海狸,它跑进对象存储,一次取回一条数据。它每取得一条数据就带回来给你,而你又要求它去取下一条。游标可以双向移动,也可以被限制在一定的数据“范围”内。
和CRUD一样的是,游标也是在事物内使用的。也需要获取一个事物,从这个事物中获取一个存储对象,然后在此基础上打开一个游标。
var transaction = db.transaction(["people"], "readonly");
var objectStore = transaction.objectStore("people");
//打开一个游标
var cursor = objectStore.openCursor();
cursor.onsuccess = function(e) {
//游标每次只拿取一条数据,res为本次拿到的数据,如果需要所有数据,可以定义一个全局的数据进行push操作
var res = e.target.result;
//如果res未定义,表示已经到达了游标末尾
if(res) {
//continue方法告诉游标继续去获取下一条
res.continue();
}
}
//触发此事件就表示游标遍历已经完成,拿到了
cursor.oncomplete = function(){
}
5.获取指定范围内的数据
· 游标适用于获取所有数据,如果希望只获取部分指定范围内的数据,可以根据索引获取,索引以数据的某个属性为基础,可以从所有数据中筛选出位于指定索引范围内的数据。
· 使用范围和使用游标不同,范围是在索引上打开游标,而不是在对象上。
· 范围是由IDBKeyRange API创建的,该API提供了
· upperBound("上限")
· lowerBound ("下限")
· bound ("上限","下限")
· only("指定值")
PS:复杂检索不是indexedDB擅长的事件,如:检索id为0-10之间且code为200的数据,indexedDB无法做到。
var maxNum = 10;
var minNum = 2;
if(maxNum != "" && minNum != "") {
range = IDBKeyRange.bound(minNum, maxNum);
} else if(minNum == "") {
range = IDBKeyRange.upperBound(maxNum);
} else {
range = IDBKeyRange.lowerBound(minNum);
}
//创建事物并获取存储对象
var transaction = db.transaction(["people"],"readonly");
var store = transaction.objectStore("people");
//获取索引
var index = store.index("id");
var arr = [];
//在指定范围内打开游标成功触发
index.openCursor(range).onsuccess = function(e) {
var cursor = e.target.result;
if(cursor) {
arr.push(cursor)
cursor.continue();
}
}
//游标遍历完成
transaction.oncomplete = function() {
//没有符合的数据
if(arr.length == 0){
}
}
6. 关于indexedDB的更多内容
6.1 存储数组并根据数组中的指定项来获取数据
如果希望在数组的基础上定义索引,需要使用multiEntry选项
objectStore.createIndex("hobbies", "hobbies", {unique:false, multiEntry:true});
如果希望根据数组中的某一项来进行检索的话,只需要用到IDBKeyRange.only()
//定义检索范围 var range = IDBKeyRange.only(hobby); //创建事物并定义只读类型 var transaction = db.transaction(["people"],"readonly"); //请求存储对象 var store = transaction.objectStore("people"); //获取索引 var index = store.index("hobbies"); //打开游标成功 index.openCursor(range).onsuccess = function(e) { var cursor = e.target.result; if(cursor){ //继续拿取下一条数据 cursor.continue(); } } //游标遍历完成 transaction.oncomplete = function(){ }
6.2 计算数据量
//获取存储对象为logsOs的数据量
db.transaction(["people"],"readonly").objectStore("people").count().onsuccess =function(event) {
console.log('total is '+event.target.result);
}
7.indexedDB完整案例代码
//判断浏览器是否支持indexedDB
function idbOK() {
return "indexedDB" in window;
}
//全局数据库对象
var db = null;
$(document).ready(function() {
if(!idbOK()) return; //不支出
//打开数据库,设置数据库名称为ora_db1,版本号为1
var openRequest = indexedDB.open("ora_db1",1);
//第一次访问时触发或数据库版本变更后触发
openRequest.onupgradeneeded = function(e){
//获取数据库对象本身
var thisDB = e.target.result;
//判断当前存储中是否存在logs对象,不存在就创建
if(!thisDB.objectStoreNames.contains("people")){
//创建存储对象和主键
var peopleOs = thisDB.createObjectStore("people",{keyPath: "id", autoIncrement:true})
//创建索引 unique指索引值是否唯一,设置数组为索引时需要将multiEntry设为true
peopleOs.createIndex("name","name",{unique:false});
peopleOs.createIndex("id","id",{unique:true});
peopleOs.createIndex("hobbies","hobbies",{unique:false,multiEntry:true});
}
//判断当前存储中是否存在notes对象,不存在就创建
if(!thisDB.objectStoreNames.contains("notes")){
thisDB.createObjectStore("notes");
}
};
//数据库打开成功
openRequest.onsuccess = function(e){
db = e.target.result;
}
//数据库打开失败
openRequest.onerror = function(e) {
}
}
//给存储对象中添加数据
function addPeople(e) {
//获取事务,设置存储对象和类型
var transaction = db.transaction(["people"],"readwrite");
//请求objectStore
var store = transaction.objectStore("people");
//定义数据
var person = {
id:1,
name:"张三",
ssn:"166575758332344",
sex:"男",
email:"56778899@qq.com"
hobbies:["电影","做饭","美食"],
}
//添加数据并设置主键
var request = store.add(person);
//失败
request.onerror = function(e) {
console.log("Error",e.target.error.name);
}
//成功
request.onsuccess = function(e) {
console.log("Woot! Did it");
}
}
//删除
function removePeople(){
//获取事务,设置存储对象和类型
var transaction = db.transaction(["people"],"readwrite");
//请求objectStore
var store = transaction.objectStore("people");
//根据主键删除数据
var request = store.delete(key);
//失败
request.onerror = function(e) {
console.log("Error",e.target.error.name);
}
//成功
request.onsuccess = function(e) {
console.log("Woot! Did it");
}
}
//更新数据
function updatePerson(e) {
//获取事务,设置存储对象和类型
var transaction = db.transaction(["people"],"readwrite");
//请求objectStore
var store = transaction.objectStore("people");
//定义数据
var person = {
id:1,
name:"张三",
ssn:"166575758332344",
sex:"男",
email:"56778899@qq.com"
hobbies:["电影","做饭","美食"],
}
//根据id更新数据
var request = store.put(person,key);
//失败
request.onerror = function(e) {
console.log("Error",e.target.error.name);
}
//成功
request.onsuccess = function(e) {
console.log("Woot! Did it");
}
}
//获取数据,根据主键读取
function getPeople(){
//获取事务,设置存储对象和类型
var transaction = db.transaction(["people"],"readonly");
//请求objectStore
var store = transaction.objectStore("people");
//根据主键获取数据
var request = store.get(key);
//失败
request.onerror = function(e) {
console.log("Error",e.target.error.name);
}
//成功
request.onsuccess = function(e) {
console.log("Woot! Did it");
}
}
//获取全部数据----游标(每次只能获取一条数据)
function getAllPeople(){
//获取事务,设置存储对象和类型
var transaction = db.transaction(["people"],"readonly");
//请求objectStore
var store = transaction.objectStore("people");
//打开游标
var cursor = people.openCursor();
let allData = [];
cursor.onsuccess = function(e){
var cursor = e.target.result;
if(cursor){
//继续去获取下一条
allData.push(cursor);
cursor.continue();
}
}
//游标遍历完成
transaction.oncomplete = function(){
console.log(allData)//打印全部数据
}
}
//获取指定范围内数据
function search_people(){
var maxNum = 10;
var minNUm = 2;
var range;
if(minNUm != "" && maxNum != "") {
range = IDBKeyRange.bound(lower, maxNum); //在上限和下限范围内
} else if(minNUm == "") {
range = IDBKeyRange.upperBound(maxNum);//上限
} else {
range = IDBKeyRange.lowerBound(minNUm); //下限
//range = IDBKeyRange.only(minNUm); //只查询符合minNum的数据
}
var transaction = db.transaction(["people"],"readonly");
var store = transaction.objectStore("people");
var index = store.index("id");
var arr = [];
index.openCursor(range).onsuccess = function(e) {
var cursor = e.target.result;
if(cursor) {
arr.push(cursor);
cursor.continue();
}
}
//游标遍历完成
transaction.oncomplete = function() {
if(arr.length > 0){
console.log(arr) //符合范围内的数据
}
}
}
//获取数组总量
db.transaction(["people"],"readonly").objectStore("people").count().onsuccess =
function(event) {
console.log('total is '+event.target.result);
}