你真的了解数据存储吗

289 阅读10分钟

慕锐前端团队原创,欢迎各位点击右上角关注我们,了解更多前端知识

冯名片.png

前言

前端的数据存储方式,你除了用过 Cookies、localStorage 和 sessionStorage 外,还有用过其它的存储方式么?其实除了前面提到的 3 种存储方式,目前主流的浏览器还支持 Web SQL 和 IndexedDB。

前端数据存储方案主要有 Cookie、LocalStorage、SessionStorage、IndexedDB、Web SQL 五种。下图为 Chrome 浏览器的存储方案

1636079695(1).png

LocalStorage

是一种持久化的存储方式,如果不手动清除,数据就永远不会过期。它也是采用Key - Value的方式存储数据

特点

  • 保存的数据长期存在,下一次访问该网站的时候,网页可以直接读取以前保存的数据。
  • 大小为5M左右。
  • 仅在客户端使用,不和服务端进行通信。
  • 存储的信息在同一域中是共享的。
  • localStorage本质上是对字符串的读取,如果存储内容多的话会消耗内存空间,会导致页面变卡。

使用方法

setItem(key:string, value:string)

向localStorage 添加/更改 数据,该方法接受两个参数,key和value,类型都为字符串

localStorage.setItem('num', 100)

如果传入对象,则需要将对象转为JSON

var obj = { 
    color:'#fff', 
    size:11 ,
} 
localStorage.setItem('obj',JSON.stringify(obj))

getItem(key:string)

返回localStorage中当前key所对应的value值,返回值的类型为字符串类型

var num = localStorage.getItem('num');
console.log(num); //100
console.log(typeof(num)); //sring

removeItem(key:string)

接受一个键名作为参数,会从给定的Storage对象中删除该键名(如果存在)。

如果没有与该给定键名匹配的项,则此方法将不执行任何操作。

localStorage.removeItem('num')

key()

接受一个数值 n 作为参数,返回存储对象第 n 个数据项的键名。键的存储顺序是由用户代理定义的,所以尽可能不要依赖这个方法。

var value = localStorage.key(0) 
console.log(value) //obj

clear()

用于清空存储对象里所有的键值。

localStorage.clear()

在浏览器的控制台中可查看存储在Storage的数据

QQ截图20211108155111.png

SessionStorage

sessionStorage跟localStorage差不多,都是用于存储字符型的键值。

但存储在sessionStorage中的数据将在页面会话结束时被清空,而localStorage只能手动删除

sessionStorage的方法和属性都跟localStorage一样的

var obj ={ 
fruits:'apple', 
num; 100 
} 
sessionStorage.setItem('obj',JSON.stringify(obj)); 

var value = sessionStorage.getItem('obj'); 
console.log(value); // {"fruits":"apple","number":100} 

var key = sessionStorage.key(0) 
console.log(key) //obj 

sessionStorage.removeItem('obj'); 
sessionStorage.clear();

indexedDB

indexedDB是一种将数据存储在前端的技术,可存储的数据量很大,将大量不变的数据存储在客户端,可以减少从服务器获取数据,直接从本地获取数据。也就是说,IndexedDB 就是浏览器提供的本地数据库,它可以被网页脚本创建和操作。IndexedDB 允许储存大量数据,提供查找接口,还能建立索引。

indexedDB与localStorage、sessionStorage和cookie不同的是,它们都不能存放大量数据,cookie不能超过4KB,且每次请求都会发送回服务器,localStorage和sessionStorage一般不超过4MB(不同浏览器的限制不一样),所以这些技术都不太适合存放大量的数据。

特点

  • key/value的存储方式  IndexedDB 内部采用对象仓库(object store)存放数据。所有类型的数据都可以直接存入,包括 JavaScript 对象。对象仓库中,数据以"键值对"的形式保存,每一个数据记录都有对应的主键,主键是独一无二的,不能有重复,否则会抛出一个错误。
  • 异步调用

IndexedDB是使用异步调用的,当我们存储一个较大的数据时,不会因为写入数据慢而导致页面阻塞。

  • 支持事务 IndexedDB支持事务,如果有用过mysql和mongoDB的人就很清楚了,能确保我们多个操作只要其中一步出现问题,可以整体回滚。

  • 同源限制

IndexedDB和localStorage一样,都是有同源策略的问题,不能跨协议、端口、域名使用。

  • 储存空间

IndexedDB我认为最最最大的优势在于存储空间相比localStorage要大得多,一般来说不少于250MB。

  • 支持二进制

IndexedDB不但可以存储对象,字符串等,还可以存储二进制数据。

什么是对象仓库(object store)?

介绍对象仓库前说一下indexedDB数据库这个概念,数据库是一系列相关数据的容器。每个域名(严格的说,是协议 + 域名 + 端口)都可以新建任意多个数据库。IndexedDB 数据库有版本的概念。同一个时刻,只能有一个版本的数据库存在。如果要修改数据库结构(新增或删除表、索引或者主键),只能通过升级数据库版本完成。

而每个数据库包含若干个对象仓库(object store)。它类似于关系型数据库的表格。

使用方法

1. 打开数据库

open(name, version)

使用open方法打开一个数据库,并返回一个IDBOpenDBRequset对象。

第一个参数,数据库的名字,类型为字符串,

第二个参数,版本号,可选,数字类型

var openRequset = window.indexedDB.open('testDB', 1);

如果打开的数据库没有,将会自动创建,创建好数据库后,将在调试工具上可以看到刚创建的数据库

微信截图_20211105135948.png

它有四种回调方法

  • success: 打开成功
  • error:打开失败
  • upgradeneeded:第一次打开该数据库,或者数据库版本发生变化
  • blocked:上一次的数据库连接还未关闭

success事件发生后,从openRequest.result属性可以拿到已经打开的IndexedDB数据库对象

2. 添加数据

add(item)

IndexedDB使用的是对象存储仓库,而不是使用表来存储。每当一个值被存储进一个对象存储空间时,它会被和一个键相关联。

新建的数据库想要进行其他操作,需要在upgradeneeded事件的监听函数里面完成,因为这时版本从无到有,所以会触发这个事件。

通常,新建数据库以后,第一件事是新建对象仓库。

openRequset.onupgradeneeded = e => {     
    var db = openRequset.result;
    var objectStore;
    objectStore = db.createObjectStore('dataList', { keyPath: 'id' }); 
}

数据库新建成功以后,新增一张叫做dataList的表格,主键是id。

没有合适作为主键的属性,那么可以让 IndexedDB 自动生成主键。

objectStore = db.createObjectStore('dataList', { autoIncrement: true });

通过事务添加数据

const cat = {     
    id: 1,     
    name: 'Tom',     
    color: 'gray' 
} 

openRequset.onsuccess = e => {     
    var db = openRequset.result;     
    var dataObjectStore = db.transaction('dataList', 'readwrite').objectStore('dataList');
    dataObjectStore.add(cat); 
}

3. 获取数据

get(key)

参数为主键的值

openRequset.onsuccess = e => {     
    var db = openRequset.result;     
    var dataObjectStore = db.transaction('dataList''readwrite').objectStore('dataList');
    var value = dataObjectStore.get(1)     
    value.onsuccess = e => {         
        console.log('name:' + value.result.name);   //Tom  
    }     
    value.onerror = e => console.log(e);
}

4. 更新数据

put(item, key)

该方法接受两个参数,第一个参数为新数据,第二个参数为主键,该参数可选,且只在自动递增时才有必要提供,因为那时主键不包含在数据值里面。

如果对应的键值不存在,则插入一条新的记录。

openRequset.onsuccess = e => {     
    var db = openRequset.result;     
    var dataObjectStore = db.transaction('dataList', 'readwrite').objectStore('dataList');
    dataObjectStore.put({ 
        id:1, 
        name:'Jerry', 
        color:'orange' 
    })   
    
    dataObjectStore.onerror = e => console.log(e);     
    dataObjectStore.onsuccess = e => console.log(e); 
}

如何没有指定的主键

dataObjectStore.put({ 
    name:'Jerry', 
    color:'orange' 
}, 1)

5. 删除数据

delete(key)

openRequset.onsuccess = e => {     
    var db = openRequset.result;                                                               
    var dataObjectStore = db.transaction('dataList', 'readwrite').objectStore('dataList');
    dataObjectStore.delete(1); 
    
    dataObjectStore.onerror = e => console.log(e);     
    dataObjectStore.onsuccess = e => console.log(e); 
}

6. 使用索引

如果在获取数据时,数据量很大,不能及时找到目标数据的key值时,用get()方法获取数据会显得繁琐

这时候我们使用索引,使用索引前我们需要建立索引,在创建对象仓库时建立索引

openRequset.onupgradeneeded = e => {     
    var db = openRequset.result; 
    var objectStore; 
    objectStore = db.createObjectStore('dataList', { keyPath: 'id' });
    
    objectStore.createIndex('name', 'name', { unique: false });
}

上面代码中,IDBObject.createIndex()的三个参数分别为索引名称、索引所在的属性、配置对象(说明该属性是否包含重复的值)。

这样就可以从name找到对应的数据了

openRequset.onsuccess = e => {     
    var db = openRequset.result;     
    var dataObjectStore = db.transaction('dataList''readwrite').objectStore('dataList');
    var index = dataObjectStore.index('name');
    var value = index.get('Tom');
    
    value.onsuccess = e => {         
        console.log('name:' + value.result.color);   //gray  
    }     
    value.onerror = e => console.log(e);
}

7. 删除数据库

deleteDatabase(name)

var DBDeleteRequest = window.indexedDB.deleteDatabase('testDB');

也可以在控制台中手动删除

Web SQL

Web SQL 数据库 API 并不是 HTML5 规范的一部分,但是它是一个独立的规范,引入了一组使用 SQL 操作客户端数据库的 APIs。

核心方法

  • openDatabase:这个方法使用现有的数据库或者新建的数据库创建一个数据库对象。
  • transaction:这个方法让我们能够控制一个事务,以及基于这种情况执行提交或者回滚。
  • executeSql:这个方法用于执行实际的 SQL 查询。

打开数据库

var db = openDatabase('testDB', '1.0', 'Test DB', 2 * 1024 * 1024);

该方法有五个参数

  1. 数据库名称
  2. 版本号
  3. 描述文本
  4. 数据库大小
  5. 创建回调 如果数据库不存在,就会新建一个数据库

插入数据

var db = openDatabase('testDB', '1.0', 'Test DB', 2 * 1024 * 1024); 
db.transaction(function (tx) {     
    tx.executeSql('CREATE TABLE IF NOT EXISTS LOGS (id unique, log)');
    tx.executeSql('INSERT INTO LOGS(id, log) VALUES (1,"Tom")');     
    tx.executeSql('INSERT INTO LOGS(id, log) VALUES (2,"Jerry")');     
    // tx.executeSql('INSERT INTO LOGS(id, log) VALUES (?,?)', [e_id, e_value]); 
})

在名为testDB的数据库上建立一张名为LOGS的表

在LOGS表中插入两个数据

微信截图_20211105173029.png

读取数据

db.transaction(function(tx){     
    tx.executeSql('SELECT * FROM LOGS', [], function(tx,results){ 
        console.log(results.rows);      {{id1log'Tom'},{id: 2, log: 'Jerry'}}   
        console.log(results.rows.item(2).log);   //Jerry  
    },null) 
})

更新数据

db.transaction(function(tx){     
    tx.executeSql('UPDATE LOGS SET log=\'cat\' WHERE id=1')     
    // tx.executeSql('UPDATA LOGS SET log=\'cat\' WHERE id=?',[id]) 
})

删除数据

db.transaction(function(tx){     
    tx.executeSql('DELETE FROM LOGS WHERE id=2')     
    // tx.execteSql('DELETE FROM LOGS WHERE id=?',[id]) 
})

Cookie

HTTP协议本身是无状态的。什么是无状态呢,即服务器无法判断用户身份。Cookie实际上是一小段的文本信息(key-value格式)。客户端向服务器发起请求,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。客户端浏览器会把Cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器检查该Cookie,以此来辨认用户状态。

cookie就是便利贴,每当你访问一个网站,该网站的服务器就会给你发一张便利贴。当他需要记一些和你相关的事情的时候(比如你的姓名,浏览记录等),就会写在你的便利贴上。同时,记在这张便利贴上的东西不能太重要,因为你可能会弄丢,也可能会主动撕掉它。服务器也不会心疼,再给你发一张就是了。

www.zhihu.com/question/46…

cookie工作流程

客户端发送一个请求到服务器 --> 服务器发送一个HttpResponse响应到客户端,其中包含Set-Cookie的头部 --> 客户端保存cookie,之后向服务器发送请求时,HttpRequest请求中会包含一个Cookie的头部 --> 服务器返回响应数据

cookie属性

  • NAME=VALUE 键值对,可以设置要保存的 Key/Value,注意这里的 NAME 不能和其他属性项的名字一样
  • Expires 过期时间,在设置的某个时间点后该 Cookie 就会失效
  • Domain 生成该 Cookie 的域名,如 domain="www.baidu.com"
  • Path 该 Cookie 是在当前的哪个路径下生成的,如 path=/wp-admin/
  • Secure 如果设置了这个属性,那么只会在 SSH 连接时才会回传该 Cookie

参考文献

www.w3.org/TR/webdatab…

www.jianshu.com/p/6fc9cea6d…

wangdoc.com/javascript/…

www.cnblogs.com/st-leslie/p…

招贤纳士

甜有一百种方式,最重要的一种,就是每天在公司看到你。慕锐前端团队,隶属于杭州慕锐科技研发部,办公位坐拥一线江景。技术团队现有人员40余人,他们来自浙江大学、北京大学、Carleton、NUS、The George Washington University等多所知名院校;同时,公司在业务上也与中国美术学院、浙江大学有深度合作关系。团队建有完善的研发流程闭环、规范的规范制度及文档服务,除了日常业务对接,在可视化、物料库、工程化系统、前后端协同、数据埋点、自动化测试、Web性能、合规检测、搭建能力及构建部署等方向进行技术探索及创新实战,实现了一系列内部产品技术。

如果你正在为怀才不遇而烦恼,正好,我们老板怀财不遇;如果你正在为无法突破自己而苦恼,正好,我们这里有诸多让你实践的机会。望远镜最远看到137亿光年,你的潜力,测算不完。任何时间,我们来聊聊chenwanjun@meprint.com