随着移动网络的兴起,WebApp也不在是新的话题了,以前Web与App原生系统最大3个差别在于:
- 用户体验,主要涉及到操作的平滑性,软件整体的性能。
- 本地存储能力,原生App能很好的发挥本地存储的能力,将不常更新的数据长期存储在本地。
- 操作系统资源,原生App能很好操作其他的app,最为常见的功能是共享到微信朋友圈。
这几年WebApp一直在想办法攻克这些难关,上述两个问题在随着技术的不断升级也有了很好的改观,对于用户体验,目前SPA(单页面)框架如:React、VUE、Angular都能很好的实现,配合着HTML5的离线缓存技术,让WebApp操作更加的平滑。那么对于本地存储来说,也有了很好的突破。本文将针对本地存储介绍一下HTML5时代带来的本地化存储技术, localStorage 、sessionStorage、WebSQL、indexedDB等。
1. localStorage与sessionStorage
localstorage、sessionStorage是浏览器家族最早支持的本地缓存。它们的有什么区别呢?
- localStorage 用于持久化的本地存储,除非主动删除数据,否则数据是永远不会过期的。
- sessionStorage 用于本地存储一个会话(session)中的数据,这些数据只有在同一个会话中的页面才能访问并且当会话结束后数据也随之销毁。因此sessionStorage不是一种持久化的本地存储,仅仅是会话级别的存储。
1.1 web storage(localStorage和sessionStorage)和cookie的区别
Web Storage的概念和cookie相似,区别是它是为了更大容量存储设计的。Cookie的大小是受限的,并且每次你请求一个新的页面的时候Cookie都会被发送过去,这样无形中浪费了带宽,另外cookie还需要指定作用域,不可以跨域调用。除此之外,Web Storage拥有setItem,getItem,removeItem,clear等方法,不像cookie需要前端开发者自己封装setCookie,getCookie。但是Cookie也是不可以或缺的:Cookie的作用是与服务器进行交互,作为HTTP规范的一部分而存在 ,而Web Storage仅仅是为了在本地“存储”数据而生。
各个浏览器对于Web Storage存储量大致如下(单位字节):
IE 9 > 4999995 + 5 = 5000000
firefox 22.0 > 5242875 + 5 = 5242880
chrome 28.0 > 2621435 + 5 = 2621440
safari 5.1 > 2621435 + 5 = 2621440
opera 12.15 > 5M (超出则会弹出允许请求更多空间的对话框)
1.2 html5 web storage的浏览器支持情况
浏览器的支持除了IE7及以下不支持外,其他标准浏览器都完全支持(ie及FF需在web服务器里运行)。
1.3 localStorage和sessionStorage操作
localStorage和sessionStorage都具有相同的操作方法,例如setItem、getItem和removeItem等
- setItem存储value
用途:将value存储到key字段用法:.setItem( key, value)代码示例:
sessionStorage.setItem("key", "value");
localStorage.setItem("site", "js8.in");
- getItem获取value
用途:获取指定key本地存储的值用法:.getItem(key)代码示例:
var value = sessionStorage.getItem("key");
var site = localStorage.getItem("site");
- removeItem删除key
用途:删除指定key本地存储的值用法:.removeItem(key)代码示例:
sessionStorage.removeItem("key");
localStorage.removeItem("site");
- clear清除所有的key/value
用途:清除所有的key/value用法:.clear()代码示例:
sessionStorage.clear();
localStorage.clear();
- 其他操作方法:点操作和[]
web Storage不但可以用自身的setItem,getItem等方便存取,也可以像普通对象一样用点(.)操作符,及[]的方式进行数据存储,像如下的代码:
var storage = window.localStorage;
storage.key1 = "hello";
storage["key2"] = "world";
console.log(storage.key1); console.log(storage["key2"]);
- localStorage和sessionStorage的key和length属性实现遍历
sessionStorage和localStorage提供的key()和length可以方便的实现存储的数据遍历,例如下面的代码:
var storage = window.localStorage;
for (var i=0, len = storage.length; i < len; i++){
var key = storage.key(i);
var value = storage.getItem(key);
console.log(key + "=" + value);
}
注意事项:localStorage和sessionStorage只支持字符串的数据存储,不能存储javascript的对象以及数据流等数据。
2.WebSQL与indexedDB
WebSQL和IndexedDB是最新的HTML5本地缓存技术,拥有着比localStorage和sessionStorage更强大的功能,WebSQL和IndexedDB可以存储更多类型的数据,如数据流,图片,视频等。由于WebSQL在浏览器的支持没有IndexedDB好,所以本文主要介绍IndexedDB.
2.1 IndexedDB
IndexedDB 是一种可以让你在用户的浏览器内持久化存储数据的方法。特点如下:
- 支持事务、游标、索引等数据库操作
- 一般浏览器会分配50M-250M不等的内存
- 持久化存储,清除浏览器缓存不会被删除(localStorage是会被删除的)
- 支持多种数据格式:arrayBuffer、String、Object、Array、File、Blob、ImageData都ok
- 不支持跨域,一个域可以有多个数据库
- 开发中需要谨记的一个特性:异步操作,换句话说,所有操作都需要在回调函数中完成
- 存储空间大,IndexedDB的存储空间比localStorage大得多,一般来说不少于250MB。IE的储存上限是250MB,Chrome和Opera是硬盘剩余空间的某个百分比,Firefox则没有上限。
2.2 IndexedDB兼容性
前端在学一项技术时候,往往先关注它的兼容性,再牛的技术,不兼容也是用不到项目中的。
这是can i use中指示的情况,我实测的结果是: * PC端chrome 54上有特殊表现(在第一次打开indexedDB时,upgradeneeded事件不触发),firfox表现正常 * 移动端ios8-ios10 safari支持,但是X5内核不支持; Android上X5内核支持
2.3 IndexedDB使用
IndexedDB在使用上需要调用HTML5提供的API,过程略有些麻烦。因此本文推荐一个工具类:localForage,这个工具类库是firefox公司开发和维护的开源类库。封装了WebSQL、IndexedDB以及localstorage三种存储模式的API,让使用者更好的来选择存储模式。然而它对外释放的API是统一的,简单的。
简单对比一下IndexedDB原生API和localForage的API的使用方法,代码如下:
IndexedDB原生API
// IndexedDB.
var db;
var dbName = "dataspace";
var users = [ {id: 1, fullName: 'Matt'}, {id: 2, fullName: 'Bob'} ];
var request = indexedDB.open(dbName, 2);
request.onerror = function(event) {
// 错误处理
};
request.onupgradeneeded = function(event) {
db = event.target.result;
var objectStore = db.createObjectStore("users", { keyPath: "id" });
objectStore.createIndex("fullName", "fullName", { unique: false });
objectStore.transaction.oncomplete = function(event) {
var userObjectStore = db.transaction("users", "readwrite").objectStore("users");
}
};
var transaction = db.transaction(["users"], "readwrite");
// 所有数据都添加到数据后调用
transaction.oncomplete = function(event) {
console.log("All done!");
};
transaction.onerror = function(event) {
// 错误处理
};
var objectStore = transaction.objectStore("users");
for (var i in users) {
var request = objectStore.add(users[i]);
request.onsuccess = function(event) {
// 里面包含我们需要的用户信息
console.log(event.target.result);
};
}
localForage API实现
// 保存用户信息
var users = [ {id: 1, fullName: 'Matt'}, {id: 2, fullName: 'Bob'} ];
localForage.setItem('users', users, function(result) {
console.log(result);
});
是不是简单了很多?下面针对localForage的使用做一下详细的介绍。
3.localForage的使用 API 地址
localForage的使用非常简单,在使用前需要进行一个配置工作,当然也可以不做配置工作。在不做配置的时候,localForage默认使用IndexedDB作为存储模式。通过查看localForage的源码来看看配置的缺省值吧,代码如下:
//默认的驱动顺序,首选是IndexedDB,其次是WebSQL,最后是Localstorage
var DefaultDriverOrder = [
DriverType.INDEXEDDB
,DriverType.WEBSQL
,DriverType.LOCALSTORAGE
];
//对外释放的方法api
var LibraryMethods = [
'clear'
,'getItem'
,'iterate'
,'key'
,'keys'
,'length'
,'removeItem'
,'setItem'
];
//默认的配置
var DefaultConfig = {
description: '',
driver: DefaultDriverOrder.slice(),
name: 'localforage',//默认数据库名称
size: 4980736,//目前只支持WebSQL可以设置大小
storeName: 'keyvaluepairs',
version: 1.0
};
3.1 Config 配置方法
可以通过Config方法来设置默认的值,如驱动、数据库名称、数据集合名称、存储大小等。详细内容如下:
属性 | 默认值 | 描述 |
---|---|---|
driver | [localforage.INDEXEDDB,localforage.WEBSQL,localforage.LOCALSTORAGE] | 要使用的首选驱动程序。 格式与传递给setDriver的格式相同 |
name | localforage | 数据库名称 |
size | 4980736(字节) | 数据库的大小,目前只有WebSQL才有效 |
storeName | keyvaluepairs | 集合名称 |
version | 1.0 | 数据库的版本号 |
description | 空字符串 | 数据库的描述,基本上用于开发人员的使用 |
例子如下:
// This will rename the database from "localforage"
// to "Hipster PDA App".
localforage.config({
name: 'Hipster PDA App'
});
// This will force localStorage as the storage
// driver even if another is available. You can
// use this instead of `setDriver()`.
localforage.config({
driver: localforage.LOCALSTORAGE,
name: 'I-heart-localStorage'
});
// This will use a different driver order.
localforage.config({
driver: [localforage.WEBSQL,
localforage.INDEXEDDB,
localforage.LOCALSTORAGE],
name: 'WebSQL-Rox'
});
3.2 getItem 方法
从存储库获取项目,并将结果提供给回调。 如果键不存在,getItem()将返回null。
getItem(key, successCallback)
例子如下:
//promise方式
localforage.getItem('somekey').then(function(value) {
// This code runs once the value has been loaded
// from the offline store.
console.log(value);
}).catch(function(err) {
// This code runs if there were any errors
console.log(err);
});
// Callback方式
localforage.getItem('somekey', function(err, value) {
// Run this code once the value has been
// loaded from the offline store.
console.log(value);
});
3.3 setItem 方法
将数据保存到离线数据库
setItem(key, value, successCallback)
例子如下:
localforage.setItem('somekey', 'some value').then(function (value) {
// Do other things once the value has been saved.
console.log(value);
}).catch(function(err) {
// This code runs if there were any errors
console.log(err);
});
// Unlike localStorage, you can store non-strings.
localforage.setItem('my array', [1, 2, 'three']).then(function(value) {
// This will output `1`.
console.log(value[0]);
}).catch(function(err) {
// This code runs if there were any errors
console.log(err);
});
// You can even store binary data from an AJAX request.
req = new XMLHttpRequest();
req.open('GET', '/photo.jpg', true);
req.responseType = 'arraybuffer';
req.addEventListener('readystatechange', function() {
if (req.readyState === 4) { // readyState DONE
localforage.setItem('photo', req.response).then(function(image) {
// This will be a valid blob URI for an tag.
var blob = new Blob([image]);
var imageURI = window.URL.createObjectURL(blob);
}).catch(function(err) {
// This code runs if there were any errors
console.log(err);
});
}
});
3.4 removeItem方法
根据key删除数据的方法
例子如下:
localforage.removeItem('somekey').then(function() {
// Run this code once the key has been removed.
console.log('Key is cleared!');
}).catch(function(err) {
// This code runs if there were any errors
console.log(err);
});
3.5 clear 方法
清空数据的方法
例子如下:
localforage.clear().then(function() {
// Run this code once the database has been entirely deleted.
console.log('Database is now empty.');
}).catch(function(err) {
// This code runs if there were any errors
console.log(err);
});
3.6 其他方法
还有length、key、keys、iterate等方法,请详见 API