慕锐前端团队原创,欢迎各位点击右上角关注我们,了解更多前端知识
前言
前端的数据存储方式,你除了用过 Cookies、localStorage 和 sessionStorage 外,还有用过其它的存储方式么?其实除了前面提到的 3 种存储方式,目前主流的浏览器还支持 Web SQL 和 IndexedDB。
前端数据存储方案主要有 Cookie、LocalStorage、SessionStorage、IndexedDB、Web SQL 五种。下图为 Chrome 浏览器的存储方案
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的数据
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);
如果打开的数据库没有,将会自动创建,创建好数据库后,将在调试工具上可以看到刚创建的数据库
它有四种回调方法
- 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);
该方法有五个参数
- 数据库名称
- 版本号
- 描述文本
- 数据库大小
- 创建回调 如果数据库不存在,就会新建一个数据库
插入数据
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表中插入两个数据
读取数据
db.transaction(function(tx){
tx.executeSql('SELECT * FROM LOGS', [], function(tx,results){
console.log(results.rows); {{id: 1, log: '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就是便利贴,每当你访问一个网站,该网站的服务器就会给你发一张便利贴。当他需要记一些和你相关的事情的时候(比如你的姓名,浏览记录等),就会写在你的便利贴上。同时,记在这张便利贴上的东西不能太重要,因为你可能会弄丢,也可能会主动撕掉它。服务器也不会心疼,再给你发一张就是了。
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
参考文献
招贤纳士
甜有一百种方式,最重要的一种,就是每天在公司看到你。慕锐前端团队,隶属于杭州慕锐科技研发部,办公位坐拥一线江景。技术团队现有人员40余人,他们来自浙江大学、北京大学、Carleton、NUS、The George Washington University等多所知名院校;同时,公司在业务上也与中国美术学院、浙江大学有深度合作关系。团队建有完善的研发流程闭环、规范的规范制度及文档服务,除了日常业务对接,在可视化、物料库、工程化系统、前后端协同、数据埋点、自动化测试、Web性能、合规检测、搭建能力及构建部署等方向进行技术探索及创新实战,实现了一系列内部产品技术。
如果你正在为怀才不遇而烦恼,正好,我们老板怀财不遇;如果你正在为无法突破自己而苦恼,正好,我们这里有诸多让你实践的机会。望远镜最远看到137亿光年,你的潜力,测算不完。任何时间,我们来聊聊chenwanjun@meprint.com