浏览器存储数据方式

1,738 阅读8分钟

前言:浏览器本身存储数据的方式有cookies、session storage、local storage、IndexedDB、web SQL

 

cookies

  • 存储大小
    • 4KB,纯文本格式,超过被忽略
    • 所以很少被用于存储客户端数据
  • 存储时间
    • 可以设置过期时间,expires选项
    • 会话期cookie浏览器关闭自动清除
    • 持久性cookie设置过期时间,到期后清除
  • 可以和服务器对话
    • 在我们请求服务器的时候,服务器可能会给我们返回cookie
    • 我们在请求服务器的时候也会带上cookie
    • 一般检查是否登录是看cookie
  • 可以保存的内容
    • key/vaule;cookie都是以key/value的形式存在
    • expires;cookie的最长有效时间,如果没有表示这是一个会话期cookie
    • httpOnly;设置了 HttpOnly 属性的 cookie 不能使用 JavaScript读取
    • .....(还有一些可选项)
    • 设置cookie,一次只能设置一个cookie
document.cookie = "name=yangfeng";// 可选的配置
document.cookie = 'test1=hello';
document.cookie = 'test2=world';
  • 获取cookie
const allCookies = document.cookie;
  • 删除cookies
    • 设置它的expires属性为一个过去的日期
document.cookie = 'test1=hello;expires=Thu, 01-Jan-1970 00:00:01 GMT';

sessionStorag

  • 存储大小
    • 2.5~10M
  • 存储时间
    • 页面会话期可用,关闭浏览器消失
    • 页面刷新和恢复也可用
    • 打开多个相同url的tab页面,会创建各自的session,即不可相互访问
    • 使用location.href或者window.open来打开另一个同域页面 是可以访问的
      • 先操作session再打开才会有
    • 清除缓存也会消失
  • 不可以和服务器对话
  • 可以保存任意内容
    • 键值存储,类似于对象,整数自动转化为字符串
    • 所以存储前可以先使用JSON.stringify()进行转换
  • 设置sessionStorage
    • 三种方法都可以
window.sessionStorage['name'] = 'yangfeng';
window.sessionStorage.age = 18;
window.sessionStorage.setItem('key','value')
  • 获取sessionStorage
sessionStorage.getItem("name");
sessionStorage.name
  • 删除sessionStorage
// 删除某个sessionStorage
sessionStorage.removeItem('key');
delete sessionStorage.name;
// 清除所以数据
sessionStorage.clear();

localStorage

  • 存储大小
    • 2.5~10M
  • 存储时间
    • 永久保存
    • 手动清除或者清除缓存
  • 不可以和服务器对话
  • 可以保存任意内容
  • localStorage满了的情况下仍继续存储并不会覆盖其他的值,而是直接报错(QuotaExceededError) ,当前存储的值会被清空
  • 操作方式和sessionStorage一样
  • 一个页面操作了localStorage同源的页面都可以访问和操作
  • 可以监听同源localStorage的修改(不监听本页面的操作)
window.addEventListener('storage', function(e) {
  console.log('---',e)
});

IndexedDB

  • 存储大小
    • 大于250M
  • 存储时间
    • 手动更新或删除
  • 不可以和服务器对话
  • 创建一个IndexedDB数据库
    • 使用window.indexedDB.open("MyTestDatabase")创建一个叫MyTestDatabase的数据库,open 函数的结果是一个 IDBDatabase 对象的实例 (接收两个参数)
      • 第一个参数为数据库名
      • 第二个参数为版本号,默认为1,必须是整数
    • 如果创建的时候有数据库名则直接返回直接返回,没有则新建,新建的时候会触发upgradeneeded事件
  • 创建一个对象仓库
    • 要创建一个对象仓库必须在upgradeneeded事件中
    • 版本号更新时触发upgradeneeded事件
    • 使用createObjectStore()方法创建一个对象仓库 (接收两个参数)
      • 第一个参数是对象仓库名,同一数据库中仓库名不能重复
      • 第二个参数,用于指定数据的主键
        • 是一个对象{keyPath:'xx',autoIncrement:true}
        • 如果能保证keyPath唯一可以让不设置autoIncrement

image.png  

  • 创建事务(可以存数据了)
    • 一个数据库事务通常包含了一个序列的对数据库的读/写操作
    • 在操作的时候要么全部操作成功要么全部操作失败
    • 可以使用transaction()来创建一个事务(接收两个参数)
      • 第一个参数是想要操作的仓库名称
      • 第二个参数是创建的事务模式
        • readonly表示制度
        • readwite表示读写

并非任意的对数据库的操作序列都是数据库事务。数据库事务拥有以下四个特性,习惯上被称之为ACID特性。

  • 原子性(Atomicity):事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行
  • 一致性(Consistency):事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态的含义是数据库中的数据应满足完整性约束
  • 隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行
  • 持久性(Durability):已被提交的事务对数据库的修改应该永久保存在数据库中
  • 操作数据
    • add() : 增加数据。接收一个参数,为需要保存到对象仓库中的对象。
    • put() : 增加或修改数据。接收一个参数,为需要保存到对象仓库中的对象。
    • get() : 获取数据。接收一个参数,为需要获取数据的主键值。
    • delete() : 删除数据。接收一个参数,为需要获取数据的主键值。

put 保存数据时,如果该数据的主键在数据库中已经有相同主键的时候,则会修改数据库中对应主键的对象,而使用 add 保存数据,如果该主键已经存在,则保存失败

const users = {id:'11',name:'yangf',age:18};
const request = indexedDB.open('MyTestDatabase', 1);
request.addEventListener('upgradeneeded', e => {
    const db = e.target.result;
    // 创建对象仓库
    const  objStore = db.createObjectStore('Users',{ keyPath : 'id' });


    // 使用事务的 oncomplete 事件确保在插入数据前对象仓库已经创建完毕
    objStore.transaction.oncomplete = function(event) {
        // 将数据保存到新创建的对象仓库
        const customerObjectStore = db.transaction("Users", "readwrite").objectStore("Users");
        customerObjectStore.add(users);
        const reqDelete = customerObjectStore.delete(1)
    }
});
  • 索引
    • 可以使用存储的对象为当前的存储空间创建索引
    • 这样可以使用索引来查找存储空间中的对象的值
    • 通过在创建索引时设置 unique 标记,可以确保不会有两个具有同样索引 key path 值的对象被储存
    • 使用createIndex()方法创建索引,接收三个参数
      • 第一个参数name索引名
      • 第二个参数keyPath为存储对象中的一个或多个属性(数组形式)
      • 第三个参数是一个对象参数
        • unique: 用来指定索引值是否可以重复,true不能相同,false可以相同
        • multiEntry: 当keyPath为一个数组时,如果设置multiEntry为true,则会以数组中的每个元素建立一条索引;如果是false,则以整个数组为keyPath值,添加一条索引.
    • 使用索引
      • 使用对象仓库上的index方法,通过传入一个索引名.来拿到一个索引对象
      • 如果索引不唯一,得到的是键值最小的那条数据
const users = {id:'11',name:'yangf',age:18};
const request = indexedDB.open('MyTestDatabase', 1);
request.addEventListener('upgradeneeded', e => {
    const db = e.target.result;
    // 创建对象仓库
    const  objStore = db.createObjectStore('Users',{ keyPath : 'id' });
    // 创建索引,通过姓名搜索user,可能重复所以unique设置为false
    objStore.createIndex("name", "name", { unique: false });
    // 使用事务的 oncomplete 事件确保在插入数据前对象仓库已经创建完毕
    objStore.transaction.oncomplete = function(event) {
        // 将数据保存到新创建的对象仓库
        const usersObjectStore = db.transaction("Users", "readwrite").objectStore("Users");
        usersObjectStore.add(users);
        
        // 使用索引
        var index = usersObjectStore.index("name");
        index.get("yangf").onsuccess = function(event) {
            console.log(event.target.result);
        };
    }
});
  • 使用游标
    • 使用 get() 要求你知道你想要检索哪一个键
    • 想要遍历对象存储空间中的所有值,可以使用游标
    • 使用openCursor() 函数打开游标(接收两个参数)
      • 第一个参数是范围,可以是一个对象
      • 第二个参数是方向
// 仅匹配 "yangf"
var singleKeyRange = IDBKeyRange.only("yangf");
// 匹配所有超过“Bill”的,包括“Bill”
var lowerBoundKeyRange = IDBKeyRange.lowerBound("Bill");
// 匹配所有超过“Bill”的,但不包括“Bill”
var lowerBoundOpenKeyRange = IDBKeyRange.lowerBound("Bill", true);
// 匹配所有不超过“Donna”的,但不包括“Donna”
var upperBoundOpenKeyRange = IDBKeyRange.upperBound("Donna", true);
// 匹配所有在“Bill”和“Donna”之间的,但不包括“Donna”
var boundKeyRange = IDBKeyRange.bound("Bill", "Donna", false, true);
// 使用其中的一个键范围,把它作为 openCursor()/openKeyCursor 的第一个参数
index.openCursor(boundKeyRange).onsuccess = function(event) {
  var cursor = event.target.result;
  if (cursor) {
    // 当匹配时进行一些操作
    cursor.continue();
  }
};
  • 方向可以传入的参数
next : 游标中的数据按主键值升序排列,主键值相等的数据都被读取
nextunique : 游标中的数据按主键值升序排列,主键值相等只读取第一条数据
prev : 游标中的数据按主键值降序排列,主键值相等的数据都被读取
prevunique : 游标中的数据按主键值降序排列,主键值相等只读取第一条数据
// 按照倒叙来排列
index.openCursor(boundKeyRange,'prev').onsuccess = function(event) {
  var cursor = event.target.result;
  if (cursor) {
    // 当匹配时进行一些操作
    cursor.continue();
  }
};
// 只是想改变遍历的方向,而不想对结果进行筛选,你只需要给第一个参数传入 null
usersObjectStore.openCursor(null, "prev").onsuccess = function(event) {
  var cursor = event.target.result;
  if (cursor) {
    // Do something with the entries.
    cursor.continue();
  }
};
  • 读取所有的数据
const users = [{id:'11',name:'yangf',age:18},{id:'12',name:'yangf',age:25}];
const request = indexedDB.open('MyTestDatabase', 1);
request.addEventListener('upgradeneeded', e => {
    const db = e.target.result;
    // 创建对象仓库
    const  objStore = db.createObjectStore('Users',{ keyPath : 'id' });
    // 创建索引,通过姓名搜索user,可能重复所以unique设置为false
    objStore.createIndex("name", "name", { unique: false });
    // 使用事务的 oncomplete 事件确保在插入数据前对象仓库已经创建完毕
    objStore.transaction.oncomplete = function(event) {
        // 将数据保存到新创建的对象仓库
        const usersObjectStore = db.transaction("Users", "readwrite").objectStore("Users");
        users.forEach(user=>{
            usersObjectStore.add(user);
        })
        usersObjectStore.openCursor(null, "prev").onsuccess = function(event) {
            const cursor = event.target.result;
            if (cursor) {
                // Do something with the entries.
                cursor.continue();
            }
        };
        // 使用索引
        // var index = usersObjectStore.index("name");
        // index.get("yangf").onsuccess = function(event) {
        //     console.log(event.target.result);
        // };
    }
});
  • 操作IndexedDB的时候,可以进行其他的js操作,是异步的 image.png image.png

  • 同源可以相互访问

web SQL

  • 存储大小
    • 自己定义
  • 存储时间
    • 需要手动清除

web SQL相对于indexedDB操作没有那么复杂,但是需要一定的数据库知识 使用openDatabase(名称,版本号,描述文本,大小,创建回调)创建一个数据库,返回一个对象 使用上面返回的对象可以创建事务,在事务中就可以执行一些操作了,插入、读取等

const myDb = openDatabase('myDb','1.0','test',2*1024*1024);
myDb.transaction(function(tx){
    tx.executeSql('CREATE TABLE IF NOT EXISTS LOGS (id unique, log)');
    tx.executeSql('INSERT INTO LOGS (id, log) VALUES (1, "yangf")');
    tx.executeSql('INSERT INTO LOGS (id, log) VALUES (2, "学习")');
})
myDb.transaction(function (tx) {
    tx.executeSql('SELECT * FROM LOGS', [], function (tx, results) {
       const len = results.rows.length;
       console.log("询记录条数: " + len );
    }, null);
});