隨著移動網絡的興起,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 <img> 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