html5本地存储 Web Storage + IndexedDB
在html5之前,前台存储主要就是cookie,cookie是有缺陷的,比如说没死请求都会带着数据,造成流量浪费;大小只有4k;明文传递不安全等等。所以
cookie的使用范围注定很小,存个登录状态之类的还是挺好用的,下面介绍一下Web Storage和IndexedDB
Web Storage
1.localStorage
localStorage相对于cookie来说存储空间是很大的,最大限制是5M左右,不同的浏览器限制也略有不同,而且localStorage是没有时间
限制的,就是说只要不是手动认为的删除的话,数据是可以一直存在的。但是localStorage也有着同源策略的限制,不能跨域访问。以下就是localStorage的API
```
setItem //存,存入localStorage,test为key,
// 例
localStorage.setItem('test','这是localStorage存储的字符串');
getItem //取,根据key获取指定数据
// 例
var tesr = localStorage.getItem('test');
console.log(tesr);
removeItem //删除,根据key删除指定的数据
// 例
localStorage.removeItem('test');
clear //清除,会把localStorage存储的所有数据清空
```
localStorage,只能以键值对的形式存储字符串,并且不支持object存储,所有能序列化成字符串的内容都可以使用localStorage进行存储,
比如:数组、图片、json、样式等等都可以
2.sessionStorage
sessionStorage在使用方法上和localStorage是相同的,两者的区别在于sessionStorage在关闭当前页面后就会被清空,二localStorage则会一直保存
3.cookie、localStorage和sessionStorage的区别和使用场景
区别:
1.cookie在浏览器和服务器端来回传递数据,而localStorage和sessionStorage不会自动把数据发送给服务器,仅会保存在本地。cookie会在浏览器请求头或者ajax请求头中发送cookie内容。
2.cookie可以设置过期日期,sessionStorage是会话级的数据,浏览器窗口关闭即清楚,localStorage是永久性的数据,一旦赋值,不管多长时间这值都是存在的,除非手动清除。
3.cookie的存储大小受限制,一般不超过4k,而localStorage和sessionStorage的存储大小一般不超过5M,大大提高了存储的体积。
4.sessionStorage不跨窗口,在另外一个窗口打开sessionStorage就不存在了,它只在当前窗口有效,而cookie和localStorage都是跨窗口的,即使浏览器的窗口关闭,这两个值还是存在的。
使用场景:
localStorage可以用来统计页面访问次数及本地存储少量的结构简单的数据。
sessionStorage可以用来统计当前页面元素的点击次数。
cookie一般存储登录相关信息,比如token之类的。
IndexedDB
localStorage使用简单字符串键值对在本地存储数据,方便灵活,但是对于大量结构化数据存储力不从心,
IndexedDB是为了能够在客户端存储大量的结构化数据,并且使用索引高效检索的API。
indexedDB简介
IndexedDB是HTML5-WebStorage的重要一环,是一种轻量级NOSQL数据库;一个网站可能有一个或多个 IndexedDB 数据库,每个数据库必须具有惟一的名称。一个数据库可包含一个或多个对象存储。一个对象存储(由一个名称惟一标识)是一个记录集合。每个记录有一个键 和一个值。该值是一个对象,可拥有一个或多个属性。键可能基于某个键生成器,从一个键路径衍生出来,或者是显式设置。一个键生成器自动生成惟一的连续正整数。键路径定义了键值的路径。
使用indexedDB之前,最好使用以下代码,兼容不同浏览器
```
//数据库对象
window.indexedDB =window.indexedDB||window.webikitIndexedDB||window.mozIndexedDB||window.msIndexedDB;
//数据库事务
window.IDBTransaction = window.IDBTransaction||window.webikitIDBTransaction||window.mozIDBTransaction||window.msIDBTransaction;
//数据库查询条件
window.IDBKeyRange = window.IDBKeyRange||window.webkitIDBKeyRange||window.mozIDBKeyRange||window.msIDBKeyRange;
//游标
window.IBDCursor = window.IBDCursor ||window.webkitIBDCursor ||window.mozIBDCursor ||window.msIBDCursor ;
```
IndexedDB 常用操作
1.创建/打开数据库
打开IndexedDB数据库,调用indexedDB.open方法就可以创建或者打开一个indexedDB。当存在当前需要打开的数据库时,open方法就会打开这个数据库,否则就会创建一个数据库。如下完整的处理
```
// 我们先创建一个对象,设置数据库的名称、版本之类的信息
var myDB = {
name: 'person',
version: '1',
db: null
}
openDB(myDB);
// 创建数据库方法
function openDB(myDB){
var request = indexedDB.open(myDB.name, myDB.version);
// 数据库打开失败
request.onerror = function (e) {
console.log(e.currentTarget.error.message);
};
// 数据库打开成功
request.onsuccess = function (e) {
myDB.db = e.target.result;
console.log('成功打开indexDB数据库');
};
// 创建和维护数据表时调用,数据库版本更新
request.onupgradeneeded = function (e) {
var db = e.target.result;
console.log('创建对象仓库(表)'); // site siteFolder safenote noteFolder
if (!db.objectStoreNames.contains('person')) {
// 创建object store
var store = db.createObjectStore('person', {keyPath: 'name'});
// 创建索引
/* store.createIndex('ageIndex','age',{unique:false});
store.createIndex('nameIndex','name',{unique:true});
store.createIndex('hobbyIndex','hobby',{unique:false});
store.createIndex('sexIndex','sex',{unique:false}); */
}
};
}
```
我们注意到除了onerror和onsuccess之外,还有一个奇怪的类似于回调函数的方法 – onupgradeneeded,这个方法,在我们请打开的数据库版本号和已经存在的版本号不一致的时候调用。
当然我们不能尝试打开比当前数据库版本号更低的数据库,否则走的就是onerror了
数据库创建成功了,还要有表来存储数据,但是indexDB是没有表这个概念的,IndexedDB用来存储的是objectStore,对象存储区,我习惯称之为
‘对象仓库’,一个数据库中可以包含多个objectStore,它是一个灵活的数据结构,可以存放多种类型的数据,可以把它看做是一张表,每条数据都关联着一个键,我们根据键值来获取数据。
onupgradeneeded方法中的createObjectStore()就是用来创建一个objectStore,该方法有两个参数:第一个是storeName,第二个是键的类型。这样在创建数据库的时候我们就为器添加了一个名字叫做
‘person’的objectStore
2.添加数据
数据库创建成功,objectStore也添加成功,我们准备一些数据进行添加
```
var persons = [{
name: '张三',
age: '20',
sex: '男',
hobby: '篮球'
},{
name: '李四',
age: '22',
sex: '男',
hobby: '足球'
},{
name: '王五',
age: '21',
sex: '男',
hobby: '乒乓'
},{
name: '赵六',
age: '18',
sex: '女',
hobby: '排球'
}];
```
IndexedDB在对新数据库做任何操作(增删改查)之前,都需要开始一个事务,事务决定需要对那些objectStore进行操作
事务具有三种模式
1. 只读:read,不能修改数据库数据,可以并发执行
2. 读写:readwrite,可以进行读写操作
3. 版本变更:versionchange
IndexedDB添加数据有两种方式,分别是add()和put()。两种方法的区别是,添加时遇到已存在的key时,add会返回错误,并且不会对原有数据进行
操作;而put则会覆盖原有的相同key的数据。此处我们用add()方法。
```
function addData(){
var transaction = myDB.db.transaction('person', 'readwrite'); //
var store = transaction.objectStore('person');
var request;
for(var i = 0;i < persons.length;i++){
request = store.add(persons[i]);
}
request.onsuccess = function(){
console.log('添加成功')
}
}
```
3.获取数据
objectStore中有get()方法可以通过key来获取数据,如下:
```
function getDataByKey(storeName, key) {
var transaction = myDB.db.transaction(storeName, 'readwrite');
var store = transaction.objectStore(storeName);
var request = store.get(key);
request.onsuccess = (e) => {
var queryData = e.target.result;
if (!queryData) {
console.log('没有匹配的数据');
} else {
console.log(queryData);
}
}
};
```
4.更新数据
利用put()方法的特性,我们可以用它来更新数据,首先通过key获取到数据,然后利用put()对当前数据进行重写,直接上代码
```
function updateData(){
var transaction = myDB.db.transaction(storName,'readwrite');
var store = transaction.objectStore(storName);
var request = store.get(key);
request.onsuccess = (e) => {
var person = e.target.result;
if(!person){
console.log('没有匹配的数据');
}else{
person.age = 35;
store.put(person);
}
}
}
```
5.删除数据和清空
删除数据我们可以用delete()方法,清空数据可以用clear(),如下
```
// 删除通过key
function deleteDataByKey(storName,key) {
var transaction = myDB.db.transaction(storeName,'readwrite');
var store = transaction.objectStore(storeName);
store.delete(key);
}
// 清空object store
function clearObjectStore(storeName) {
var transaction = myDB.db.transaction(storeName,'readwrite');
var store = transaction.objectStore(storeName);
store.clear();
}
```
6.索引
熟悉数据库的小伙伴们都知道索引的一个好处就是可以迅速定位数据,提高搜索速度,在indexedDB中有两种索引,一种是自增长的int值,一种是keyPath:自己指定索引列,我们重点来看看keyPath方式的索引使用.
创建object store的时候指明索引,使用objectStore的createIndex来创建索引,上面打开数据库的时候其实已经把创建索引的代码贴上了,
就不重复了,createIndex有三个参数,
```
/*
* 创建索引时的三个参数
* 1. 'ageIndex': 索引的名称
* 2. 'age': 索引的属性字段
* 3. {unique:false}: 索引的属性值是否唯一,true是唯一,false可以重复
* */
store.createIndex('ageIndex','age',{unique:false});
/*
* 利用索引获取数据
*/
function getDataByIndex(storeName){
var transaction = db.transaction(storeName);
var store = transaction.objectStore(storeName);
var index = store.index("ageIndex");
index.get('30').onsuccess=function(e){
var person = e.target.result;
console.log(person);
}
}
```
7.游标
在indexedDB中使用索引和游标是分不开的,对数据库熟悉的小伙伴们很好理解游标是什么,有了游标,我们就可以利用游标来遍历objectStore了
```
// 可以用 openCursor() 方法来打开游标
function fetchStoreByCursor(storeName){
var transaction = myDB.db.transaction(storeName);
var store = transaction.objectStore(storeName);
var request = store.openCursor();
request.onsuccess = function(e){
var cursor = e.target.result;
if(cursor){
var person = cursor.value;
console.log(person);
cursor.continue(); // curson.contine()会使游标下移,直到没有数据返回undefined
}
};
}
```
当遇到这么一种情况,比如说你要查询所有年龄在30岁的人,只是用key是肯定查不到的,这时候就需要索引和游标结合起来查询,如下
```
// 通过索引+游标查询
getDataByIndex(storName,indexes) {
var transaction = myDB.db.transaction(storName);
var store = transaction.objectStore(storName);
var index = store.index('ageIndex');
var request = index.openCursor(IDBKeyRange.only(30));
request.onsuccess = (e) => {
var cursor = e.target.result;
if(cursor){
var person = cursor.value;
console.log(person);
cursor.continue();
}else{
console.log('没有了');
}
}
}
```
这样会把年龄在30岁的所有人都列出来
8.关闭和删除数据库
```
// 关闭数据库
closeDB() {
myDB.db.close();
}
// 删除数据库
deleteDB() {
indexedDB.deleteDatabase(myDB.name);
}
```
下面是一个关于IndexedDB的小demo,各位小伙伴感觉写的有帮助的话,请用发财的小手点个赞,如果有问题欢迎留言,随时改正
```
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>IndexedDB</title>
<style type="text/css">
button {
padding: 6px 8px;
}
td {
text-align: center;
}
</style>
</head>
<body>
<table>
<tr>
<th>姓名</th>
<th>年龄</th>
<th>性别</th>
<th>爱好</th>
</tr>
<tr>
<td><input id="name" type="text"></td>
<td><input id="age" type="text"></td>
<td><input id="sex" type="text"></td>
<td><input id="hobby" type="text"></td>
</tr>
</table>
<button id="add">添加</button>
<br><br><br>
<p>通过key查找</p>
<input type="text" id="getValue">
<button id="get">查找</button>
<br><br><br>
<p>通过索引查找</p>
<select name="getByIndex" id="getByIndex">
<option value="ageIndex" selected="selected">年龄</option>
<option value="hobbyIndex">爱好</option>
<option value="nameIndex">姓名</option>
<option value="sexIndex">性别</option>
</select>
<input type="text" id="getIndex">
<button id="getByIndexBtn">查找通过索引</button>
<script type="text/javascript">
var myDB = {
name: '731815245@qq.com',
version: 2.1,
db: null
};
document.getElementById('add').onclick = () => {
var nameInput = document.getElementById('name').value || '默认张三'
var ageInput = document.getElementById('age').value || '默认20';
var sexInput = document.getElementById('sex').value || '默认男';
var hobbyInput = document.getElementById('hobby').value || '默认篮球';
var obj = {
name: nameInput,
age: ageInput,
sex: sexInput,
hobby: hobbyInput
};
indexdbHandle.addData('person',obj);
}
document.getElementById('get').onclick = () => {
var getValue = document.getElementById('getValue').value || '默认张三';
indexdbHandle.getDataByKey('person',getValue);
}
document.getElementById('getByIndexBtn').onclick = () => {
var getByIndex = document.getElementById('getByIndex').value;
var getIndex = document.getElementById('getIndex').value;
var indexes = {
index: getByIndex || 'nameIndex',
value: getIndex || '默认张三'
};
indexdbHandle.getDataByIndex('person',indexes);
}
class IndexdbHandle {
constructor(myDB) {
this.dbName = myDB.name;
this.dbVersion = myDB.version;
this.db = myDB.db;
}
// 打开数据库
openDB() {
var request = indexedDB.open(this.dbName, this.dbVersion);
console.log(request);
request.onerror = function (e) {
console.log(e.currentTarget.error.message);
};
request.onsuccess = function (e) {
myDB.db = e.target.result;
console.log('成功打开DB');
};
request.onupgradeneeded = function (e) {
var db = e.target.result;
console.log('创建表');
if (!db.objectStoreNames.contains('person')) {
// 创建object store
var store = db.createObjectStore('person', {keyPath: 'name'});
// 创建索引
store.createIndex('ageIndex','age',{unique:false});
store.createIndex('nameIndex','name',{unique:true});
store.createIndex('hobbyIndex','hobby',{unique:false});
store.createIndex('sexIndex','sex',{unique:false});
}
};
}
// 添加
addData(storName,data) {
var transaction = myDB.db.transaction(storName, 'readwrite');
var store = transaction.objectStore(storName);
store.add(data);
console.log('添加成功');
}
// 获取通过key
getDataByKey(storName,key) {
var transaction = myDB.db.transaction(storName,'readwrite');
var store = transaction.objectStore(storName);
var request = store.get(key);
request.onsuccess = (e) => {
var person = e.target.result;
if(!person){
console.log('没有匹配的数据');
}else{
console.log(person);
}
}
}
// 通过索引+游标查询
getDataByIndex(storName,indexes) {
var transaction = myDB.db.transaction(storName);
var store = transaction.objectStore(storName);
var index = store.index(indexes.index);
var request = index.openCursor(IDBKeyRange.only(indexes.value));
request.onsuccess = (e) => {
var cursor = e.target.result;
if(cursor){
var person = cursor.value;
console.log(person);
cursor.continue();
}else{
console.log('没有了');
}
}
/*index.get('默认20').onsuccess = (e) => {
var person = e.target.result;
console.log(person);
}*/
}
// 更新通过key
updateDataByKey(storName,key) {
var transaction = myDB.db.transaction(storName,'readwrite');
var store = transaction.objectStore(storName);
var request = store.get(key);
request.onsuccess = (e) => {
var person = e.target.result;
if(!person){
console.log('没有匹配的数据');
}else{
person.age = 35;
store.put(person);
}
}
}
// 删除通过key
deleteDataByKey(storName,key) {
var transaction = myDB.db.transaction(storeName,'readwrite');
var store = transaction.objectStore(storeName);
store.delete(key);
}
// 清空object store
clearObjectStore(storeName) {
var transaction = myDB.db.transaction(storeName,'readwrite');
var store = transaction.objectStore(storeName);
store.clear();
}
// 关闭数据库
closeDB() {
myDB.db.close();
}
// 删除数据库
deleteDB() {
indexedDB.deleteDatabase(this.name);
}
}
var indexdbHandle = new IndexdbHandle(myDB);
indexdbHandle.openDB();
</script>
</body>
</html>
```