1.本地存儲(chǔ)-Web Storage
2.本地存儲(chǔ)-IndexedDB
3.本地存儲(chǔ)的擴(kuò)展介紹
4.離線存儲(chǔ)-app cache
5.總結(jié)
分析存儲(chǔ)需求:
- 移動(dòng)端網(wǎng)絡(luò)環(huán)境因素
- 數(shù)據(jù)響應(yīng)慢,體驗(yàn)下降常遂,2G/3G網(wǎng)速非常慢
- 流量因素
- 客戶端存儲(chǔ) = 節(jié)省流量 = 節(jié)省金錢
- 追求原生體驗(yàn)
- 離線使用應(yīng)用
- 存儲(chǔ)應(yīng)用相關(guān)資源晾嘶、數(shù)據(jù)
Cookie 可行否? 否
因?yàn)镃ookie 的局限性:
- 存儲(chǔ)大小限制戈泼,僅 4kb 左右
- 單個(gè)域名下的數(shù)量限制淌山,50個(gè)左右
- 污染請(qǐng)求頭贮泞,浪費(fèi)流量
說明:
- cookie的作用是與服務(wù)器進(jìn)行交互熬北,作為HTTP規(guī)范的一部分而存在 氓鄙,
而Web Storage僅僅是為了在本地“存儲(chǔ)”數(shù)據(jù)而生嚎京。 - 每次你請(qǐng)求一個(gè)新的頁面的時(shí)候Cookie都會(huì)被發(fā)送過去嗡贺,這樣無形中浪費(fèi)了帶寬。
本地存儲(chǔ):localStorage 和 sessionStorage
- 相同的使用方法
- 使用 setItem(key,value) 方法設(shè)置存儲(chǔ)內(nèi)容
- 使用 getItem(key) 方法獲取存儲(chǔ)內(nèi)容
- 使用 removeItem(key) 方法刪除存儲(chǔ)內(nèi)容
- 使用 clear() 方法清除所有內(nèi)容
- 使用 length 屬性獲取存儲(chǔ)內(nèi)容個(gè)數(shù)
- 使用 key(index) 方法獲取存儲(chǔ)字段
- 不同的存儲(chǔ)時(shí)效
- localStorage 存儲(chǔ)會(huì)持久化
- sessionStorage 存儲(chǔ)會(huì)在網(wǎng)頁會(huì)話結(jié)束(標(biāo)簽頁的關(guān)閉)后失效鞍帝,只有在同一個(gè)會(huì)話中的頁面才能訪問诫睬。
- 不同的存儲(chǔ)容量
- localStorage 容量一般在 2 – 5Mb 左右
- sessionStorage 存儲(chǔ)容量不一,部分瀏覽器不設(shè)限
- Web Storage Support Test
http://dev-test.nemikor.com/web-storage/support-test/
sessionStorage和localStorage的存儲(chǔ)空間雖然較大帕涌,但是存儲(chǔ)容量仍有限制摄凡,叫做配額。
- 使用 Storage 時(shí)的注意點(diǎn):
- 存儲(chǔ)容量超出限制
- 拋出 QuotaExceededError 異常 (存儲(chǔ)值時(shí)應(yīng)使用 try catch 捕獲異常)
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>quota exceed test</title>
</head>
<body>
<button class="start-btn">start</button>
<script>
var btn = document.querySelector('.start-btn');
var data = '01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890',
id = 0,
len = data.length,
total = 0,
key;
function save() {
id = (id + 1) % 10;
key = Date.now() + '' + id;
// try...catch 主要用于捕獲代碼運(yùn)行時(shí)的異常,并進(jìn)行異常處理蚓曼。
// try 部分包含運(yùn)行時(shí)亲澡,可能出現(xiàn)異常的代碼.
// 而 catch 部分包含錯(cuò)誤發(fā)生時(shí)運(yùn)行的代碼。
try {
// try 中寫可能產(chǎn)生異常的語句
localStorage.setItem(key, data);
save();
} catch (e) {
// catch 中寫負(fù)責(zé)異常處理的語句
console.log(e.name); //QuotaExceededError
}
}
btn.addEventListener('click', save);
</script>
</body>
</html>
- 存儲(chǔ)類型的限制
- 僅能存儲(chǔ)字符串
- 注意類型轉(zhuǎn)換
JSON.stringify
纫版、JSON.parse
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>quota exceed test</title>
</head>
<body>
<button class="str" onclick="save('abc')">字符串 abc</button><br />
<button class="bool" onclick="save(false)">布爾值 false</button><br />
<button class="undef" onclick="save(window.undefined)">未定義 window.undefined</button><br />
<button class="null" onclick="save(null)">空值 null</button><br />
<button class="num" onclick="save(0)">數(shù)字 0</button><br />
<button class="arr1" onclick="save([])">數(shù)組 []</button><br />
<button class="arr2" onclick="save([1,2,3])">數(shù)組 [1,2,3]</button><br />
<button class="obj1" onclick="save({})">對(duì)象 {}</button><br />
<button class="obj2" onclick="save({x: 1})">對(duì)象 {x: 1}</button><br />
<button class="obj3" onclick="save({toString: function () {return 'toStringed'}})">對(duì)象 {toString: function () {return 'toStringed'}}</button><br />
<button class="obj4" onclick="save({toString: function () {return 100}})">對(duì)象 {toString: function () {return 100}}</button><br />
<script>
function save (data) {
localStorage.setItem('key', data)
show(localStorage.getItem('key'))
}
function show (value) {
console.log(value, '|', typeof value)
}
</script>
</body>
</html>
打印結(jié)果:
> abc | string
> false | string
> undefined | string
> null | string
> 0 | string
> | string
> 1,2,3 | string
> [object Object] | string
> [object Object] | string
> toStringed | string
> 100 | string
類型轉(zhuǎn)換:在控制臺(tái)輸入
> localStorage.setItem('key',JSON.stringify({data:1}))
< undefined
> localStorage.getItem('key')
< "{"data":1}"
> JSON.parse(localStorage.getItem('key'))
< {data: 1}
- sessionStorage 失效機(jī)制
- 刷新頁面并不會(huì)失效
location.reload()
- 相同 URL 不同標(biāo)簽頁不能共享 sessionStorage
- Web Storage 的優(yōu)化
性能與存儲(chǔ)容量大小無關(guān)床绪,與讀取次數(shù)有關(guān)
- 減少讀取 item 次數(shù)
- 單個(gè) item 中盡可能多的存儲(chǔ)數(shù)據(jù)
- 帶有過期機(jī)制的 localStorage 的功能需求
- 可以設(shè)置數(shù)據(jù)的存儲(chǔ)時(shí)間
- 過期數(shù)據(jù)清理
- 自行維護(hù)存儲(chǔ)空間
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>cache storage</title>
</head>
<body>
<script>
'use strict'
;(function () {
var ls = window.localStorage
function oops () {
return console.warn('your browser is not supported localStorage API')
}
function getItem (key) {
var data = ls.getItem(key) //在沒有數(shù)據(jù)項(xiàng)的情況下,data為null
data = JSON.parse(data)||{} //將字符串類型的data轉(zhuǎn)為對(duì)象類型的data
if (data.time === 0) {// 上句如是data = JSON.parse(data)其弊,在data.time時(shí)癞己,就會(huì)報(bào)錯(cuò)。所以調(diào)整成data = JSON.parse(data)||{}
// 持久化數(shù)據(jù)直接返回
return data.value
} else if (Date.now() > data.time) { // 判斷是否超時(shí)
//過期
ls.removeItem(key)
return ''
} else {
// 沒過期
return typeof data.value !== 'undefined' ? data.value : ''
}
}
function setItem (key, value, time) {
if (typeof key === 'undefined') {return}
var data = {
time: time ? Date.now() + time : 0,
value: value
}
data = JSON.stringify(data) //stringify方法進(jìn)行對(duì)象的字符串序列化
try {
ls.setItem(key, data)
} catch (e) {
ls.clear()
ls.setItem(key, data)
}
}
function removeItem (key) {
ls.removeItem(key)
}
function clear () {
ls.clear()
}
window.cacheStorage = {//瀏覽器支持使用localStorage瑞凑,不支持輸出提示語
getItem: ls ? getItem : oops,
setItem: ls ? setItem : oops,
removeItem: ls ? removeItem : oops,
clear: ls ? clear : clear
}
})()
</script>
</body>
</html>
IndexedDB 數(shù)據(jù)庫
1.了解indexedDB數(shù)據(jù)庫
1)indexedDB數(shù)據(jù)庫是一種事務(wù)型數(shù)據(jù)庫
2)是NoSQL數(shù)據(jù)庫
3)使用JS對(duì)象存儲(chǔ)數(shù)據(jù)
2.如何創(chuàng)建數(shù)據(jù)庫和表
- 如何創(chuàng)建數(shù)據(jù)庫和"表"末秃?
-
indexedDB.open('dbName',dbVersinNumber)
創(chuàng)建數(shù)據(jù)庫,返回 IDBOpenDBRequest 對(duì)象 - indexedDB.createObjectStore 創(chuàng)建“表”
-
indexedDB.deleteDatabase('dbName')
刪除數(shù)據(jù)庫
function createDB() {
// request 是 IDBOpenDBRequest對(duì)象籽御。
request = db.open(dbName, version)
//請(qǐng)求有三種狀態(tài)练慕,如下:
request.onsuccess = function() { // 打開數(shù)據(jù)庫成功
db = request.result;
console.log('open success');
}
request.onerror = function(e) { // 打開數(shù)據(jù)庫失敗
console.log(e.currentTarget.errormessage)
}
request.onupgradeneeded = function(e) { //請(qǐng)求數(shù)據(jù)庫版本變化時(shí)
var store = null;
db = e.target.result;
console.log('upgradeneeded');
/*
if (!db.objectStoreNames.contains(osName)) {
db.createObjectStore(osName, {autoIncrement: true}) // 創(chuàng)建的表的主建是自增型的
}
*/
if (!db.objectStoreNames.contains(osName)) {
store = db.createObjectStore(osName, { keyPath: 'id' }) // 創(chuàng)建的表以字段為主建
store.createIndex('idIndex', 'id', { unique: true }); //創(chuàng)建索引字段id唯一
store.createIndex('categoryIndex', 'category', { multiEntry: true }); //創(chuàng)建索引字段為數(shù)組
store.createIndex('hpIndex', 'hp', { unique: false });
}
}
// onsuccess事件在onupgradeneeded事件之后觸發(fā)
}
- 設(shè)置主鍵的兩種方法
- 設(shè)置自增主鍵 - {autoIncrement: true}
db.createObjectStore(osName, {autoIncrement: true})
- 取數(shù)據(jù)中字段作為主鍵 - {keyPath: 字段名}
store = db.createObjectStore(osName, { keyPath: 'id' })
3. 關(guān)于表的增刪改查
- 如何使用事務(wù)獲取表
調(diào)用IDBDatabase.transaction
方法會(huì)返回一個(gè) IDBTransaction 對(duì)象惰匙,
它含有一個(gè) objectStore 方法, 可以讓用戶通過指定模式操作數(shù)據(jù)庫中的“表”。
indexedDB
-> transaction
-> objectStore
//osName 表格名稱铃将,
var db = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
var transaction = db.transaction(osName, 'readwrite'), //打開一個(gè)事務(wù)项鬼,讀寫模式
store = transaction.objectStore(osName);
- 事務(wù)的模式
- 讀寫模式 -
readwrite
- 只讀模式(默認(rèn)) -
readonly
- 版本變更模式 -
versionchange
- 關(guān)于“表”的增刪查改的相關(guān)方法
- 增加數(shù)據(jù) –
IDBObjectStore.add
- 獲取數(shù)據(jù) –
IDBObjectStore.get
- 獲取所有數(shù)據(jù) –
IDBObjectStore.getAll
- 修改(或新增)數(shù)據(jù) –
IDBObjectStore.put
- 刪除數(shù)據(jù) –
IDBObjectStore.delete
- 清除所有數(shù)據(jù) –
IDBObjectStore.clear
這些方法都返回一個(gè)IDBRequest
對(duì)象。
function addData() {
if (!db) { alert("db"); }
// 調(diào)用 `IDBDatabase.transaction` 方法會(huì)返回一個(gè) IDBTransaction 對(duì)象劲阎,
// 它含有一個(gè) objectStore 方法, 可以讓用戶通過指定模式操作數(shù)據(jù)庫中的“表”绘盟。
var transaction, store;
transaction = db.transaction(osName, 'readwrite'); //打開一個(gè)事務(wù),讀寫模式
store = transaction.objectStore(osName) ///獲取osName指定的 object store
data.map(function(o) {
store.add(o)
// request = store.add(o) //增加數(shù)據(jù)
// if (data.length - 1 === i) {
// request.onsuccess = function () {
// console.log('alreay add all data to db')
// showCurrentData()
// }
// }
})
}
function getData(id) {
var transaction = db.transaction(osName, 'readwrite'), //打開一個(gè)事務(wù)悯仙,讀寫模式
store = transaction.objectStore(osName);
var request = store.get(id);
request.onsuccess = function() {
console.log(request.result);
}
}
function getAllData() {
var transaction = db.transaction(osName, 'readwrite'), //打開一個(gè)事務(wù)龄毡,讀寫模式
store = transaction.objectStore(osName);
var request = store.getAll();
request.onsuccess = function() {
console.log(request.result);
}
}
function updateData(id) {
var transaction = db.transaction(osName, 'readwrite'), //打開一個(gè)事務(wù),讀寫模式
store = transaction.objectStore(osName);
var request = store.get(id);
request.onsuccess = function() {
request = store.put({ //更新即可以更新也可以添加锡垄,取決于關(guān)鍵字是否有重復(fù)
name: '小花貓',
id: id,
hp: 9
});
}
}
function deleteData(id) {
var transaction = db.transaction(osName, 'readwrite'), //打開一個(gè)事務(wù)沦零,讀寫模式
store = transaction.objectStore(osName);
var request = store.delete(id);
request.onsuccess = function() {
console.log('delete success');
}
}
function clear() {
var transaction = db.transaction(osName, 'readwrite'), //打開一個(gè)事務(wù),讀寫模式
store = transaction.objectStore(osName);
var request = store.clear();
request.onsuccess = function() {
console.log('clear success');
}
}
- IDBRequest 對(duì)象
- 使用
IDBRequest.onsuccess
執(zhí)行查詢完成回調(diào) - 使用
IDBRequest.result
獲取查詢結(jié)果 - 使用
IDBRequest.onerror
執(zhí)行查詢失敗回調(diào)
4. 關(guān)于索引
- 如何創(chuàng)建索引
IDBObjectStore.createIndex
- indexName: 索引名稱
- keyPath: 索引字段货岭,可以為空或者數(shù)組(type array)
- optionParameters: 索引配置參數(shù)
store = db.createObjectStore(osName, { keyPath: 'id' }) // 創(chuàng)建的表以字段為主建
store.createIndex('idIndex', 'id', { unique: true }); //創(chuàng)建索引字段id唯一
store.createIndex('categoryIndex', 'category', { multiEntry: true }); //創(chuàng)建索引字段為數(shù)組
store.createIndex('hpIndex', 'hp', { unique: false });
- optionParameters: 索引配置參數(shù)
- unique 表示keyPath字段的數(shù)據(jù)是否唯一
2)multiEntry 表示是否為 keyPath 字段的每一項(xiàng)建立一條索引數(shù)據(jù)
- 使用索引的好處
- 可以使用存儲(chǔ)記錄中的值進(jìn)行檢索
- 索引自動(dòng)更新
- 索引數(shù)據(jù)自動(dòng)排序
- 索引的相關(guān)方法
1)查詢數(shù)據(jù) - IDBIndex.get
- 查詢所有數(shù)據(jù) - IDBIndex.getAll
- 打開游標(biāo) - IDBIndex.openCursor
function useIndexGetData() { // 索引只能查詢數(shù)據(jù)路操,并不能操作數(shù)據(jù)
var transaction = db.transaction(osName, 'readwrite'), //打開一個(gè)事務(wù),讀寫模式
store = transaction.objectStore(osName),
index = store.index('categoryIndex');
request = index.getAll('飛行');
request.onsuccess = function() {
console.log(request.result);
}
}
5. 關(guān)于游標(biāo)
- 如何創(chuàng)建游標(biāo)千贯?
IDBObjectStore/IDBIndex.openCursor
- 接收可選參數(shù) range 和 direction屯仗,指明游標(biāo)遍歷的范圍和方向
- 返回一個(gè) IDBRequet 對(duì)象,異步方法
- 該 IDBRequet 對(duì)象的結(jié)果是一個(gè) IDBCursor 對(duì)象
- IDBKeyRange 對(duì)象
1)upperBound: 指定游標(biāo)范圍的上限
2)lowerBound: 指定游標(biāo)范圍的下限
3)bound: 指定游標(biāo)范圍的區(qū)間
4)only: 指定游標(biāo)的值
key range 取值表:
- direction 參數(shù)
- next: 順序查詢
- nextunique: 順序唯一查詢
- prev: 逆序查詢
- prevunique: 逆序唯一查詢
function useCursorGetData() {
var transaction = db.transaction(osName, 'readwrite'), //打開一個(gè)事務(wù)搔谴,讀寫模式
store = transaction.objectStore(osName),
// request=store.openCursor(); //創(chuàng)建游標(biāo)
// request=store.openCursor(IDBKeyRange.only('002')); //指定游標(biāo)KeyRange魁袜,only一個(gè)
// request = store.openCursor(null, 'prev');
request = store.openCursor(IDBKeyRange.lowerBound('002'), 'prev'); //逆序查詢,游標(biāo)范圍下限是"002"
request.onsuccess = function() {
var cursor = request.result;
if (cursor) {
console.log(cursor.value);
cursor.continue();
}
}
}
6. 索引和游標(biāo)的結(jié)合使用
- 索引和游標(biāo)的優(yōu)勢(shì)
索引:可以按值搜索
游標(biāo):可以選擇遍歷順序及操作數(shù)據(jù)
function useIndexAndCursorOperateData1() { // 索引和游標(biāo)結(jié)合己沛,可以查詢和操作數(shù)據(jù)
var transaction = db.transaction(osName, 'readwrite'), //打開一個(gè)事務(wù)慌核,讀寫模式
store = transaction.objectStore(osName),
index=store.index('categoryIndex');
request=index.openCursor();
request.onsuccess = function() {
var cursor = request.result;
if (cursor) {
if(cursor.value.id==='002'){
/* // 更新數(shù)據(jù)
cursor.update({
name: '小蝙蝠',
id: '002',
hp: 10,
category:['怪物','飛行']
})
*/
// 刪除數(shù)據(jù)
cursor.delete().onsuccess=function () {
console.log('delete success');
}
}
// 更新數(shù)據(jù)和刪除數(shù)據(jù)操作都是異步操作,所以輸出可能會(huì)混亂
console.log(cursor.value);
cursor.continue();
}
}
}
function useIndexAndCursorOperateData() { // 索引和游標(biāo)結(jié)合申尼,可以查詢和操作數(shù)據(jù)
var transaction = db.transaction(osName, 'readwrite'), //打開一個(gè)事務(wù)垮卓,讀寫模式
store = transaction.objectStore(osName),
index = store.index('hpIndex');
// request = index.openCursor(IDBKeyRange.upperBound(5)); // 5(包含5)以下
request = index.openCursor(IDBKeyRange.bound(5,10,true,true)); // 范圍(5到10)
request.onsuccess = function() {
var cursor = request.result,value=null;
if (cursor) {
value = cursor.value;
value.hp += 20;
cursor.update(value);
console.log(cursor.value);
cursor.continue();
}
}
}
IndexedDB 與 Web Storage 比較
- indexedDB 的優(yōu)勢(shì):
- 存儲(chǔ)類型更加豐富
- 可實(shí)現(xiàn)高級(jí)查詢
- 可在 Web Workers 中使用
- 存儲(chǔ)容量更大
- Web Storage 的優(yōu)勢(shì)
- API 較少,更容易掌握
- 兼容性更好
- IndexedDB 的兼容性問題
1)IOS8 & 9 中 webview 不支持 indexedDB
2)Firefox 單次存儲(chǔ) Blob 數(shù)據(jù)超 50Mb 會(huì)拋出異常, 這個(gè)50M可以在IndexDB的一些API中進(jìn)行修改
3)Safari 的 indexedDB 不能用于 web workers
4)Chrome36 不支持存儲(chǔ)類型的數(shù)據(jù)
其他存儲(chǔ)方式介紹
1. WebSQL
- 關(guān)系型數(shù)據(jù)庫
- 隨HTML5規(guī)范加入师幕, 在瀏覽器端運(yùn)行的輕量級(jí)數(shù)據(jù)庫粟按。
- WebSQL 的 API 有哪些?
- openDatabase: 打開數(shù)據(jù)庫
- transaction: 獲取事務(wù),進(jìn)行數(shù)據(jù)庫操作
- executeSql: 執(zhí)行 SQL 進(jìn)行查詢
- WebSQL 的現(xiàn)狀
-
兼容性問題嚴(yán)重
W3C 已經(jīng)不再積極處理其相關(guān)規(guī)范(避免使用WebSQL)
2. Filesystem & FileWriter API
- 供我們?cè)诳蛻舳诉M(jìn)行文件的存儲(chǔ)
-
但是和WebSQL有相似的命運(yùn)霹粥,有嚴(yán)重的兼容性問題和規(guī)范被廢棄(避免使用)灭将。
3. UserData
是IE獨(dú)有的存儲(chǔ)方式:
- 只在Windows系統(tǒng)的IE中存在
- 容量不大
4. Cookie
- 存儲(chǔ)大小限制,僅 4kb 左右
- 單個(gè)域名下的數(shù)量限制后控,50個(gè)左右
- 污染請(qǐng)求頭庙曙,浪費(fèi)流量