-進(jìn)行離線檢測
-使用離線緩存
-在瀏覽器中保存數(shù)據(jù)
離線web應(yīng)用我注,就是設(shè)備在不能上網(wǎng)的情況下仍然可以運(yùn)行的應(yīng)用媳危。
離線檢測
navigator.onLine
這個(gè)屬性的關(guān)鍵是瀏覽器必須知道設(shè)備能否訪問網(wǎng)絡(luò)
true ==> 表示設(shè)備能上網(wǎng)
false ==> 表示設(shè)備離線
IE6和Safari 5+ 能夠正確檢測到網(wǎng)絡(luò)已經(jīng)斷開...略有兼容問題化借。但手機(jī)端的話蹬音,還ok
if(navigator.onLine){
console.log('能夠連接到網(wǎng)絡(luò)')
}else{
console.log('斷網(wǎng)啦')
}
online,offline
var EventUtil = {
addHandler: function (element, type, handler) {
if (element.addEventListener) { //DOM2級
element.addEventListener(type, handler, false);
} else if (element.attachEvent) { //DOM1級
element.attachEvent("on" + type, handler);
} else {
element["on" + type] = handler; //DOM0級
}
},
}
EventUtil.addHandler(window,'online',function(){
alert('online')
})
EventUtil.addHandler(window,'offline',function(){
alert('offline')
})
當(dāng)關(guān)閉網(wǎng)絡(luò)或開啟網(wǎng)絡(luò)時(shí)可以得到校驗(yàn)結(jié)果
navigator.onLine + online,offline
在頁面加載后,最好先通過navigator.onLine取得初始的狀態(tài)米绕。再通過online,offline來確定狀態(tài)是否變化垢村。
應(yīng)用緩存
appcache
Appcache是專門從瀏覽器的緩存中跟出來的一塊緩存區(qū)割疾,使用描述文件manifest file
<html manifest="offline.appcache">
描述文件的擴(kuò)展名以前推薦用manifest.現(xiàn)在推薦用appcache
CACHE MANIFEST
#Comment
index.js //描述文件中列出的都是需要下載的資源,已備離線時(shí)使用
applicationCache
雖然應(yīng)用緩存的意思是確保離線時(shí)資源可用肝断,但也有相應(yīng)的JavaScript API 讓你知道它在作什么
狀態(tài)
- 0
無緩存 - 1
閑置杈曲。未更新 - 2
檢查中。正在下載描述文件并檢查更新 - 3
下載中胸懈,即應(yīng)用混存正在下載描述文件 - 4
更新完成蛉腌∑旯可以通過swapCache()來使用 - 5
廢棄晾浴。即應(yīng)用已經(jīng)不存在
事件 - checking
在瀏覽器為應(yīng)用緩存查找更新時(shí)觸發(fā) - error
在檢查更新或下載資源期間發(fā)生錯(cuò)誤時(shí)觸發(fā) - noupdate
在檢查描述文件發(fā)現(xiàn)文件無變化時(shí)觸發(fā) - downloading
在開始下載應(yīng)用緩存資源時(shí)觸發(fā) - progress
在文件下載應(yīng)用緩存的過程中持續(xù)不斷的觸發(fā) - updateready
在頁面新的應(yīng)用緩存下載完畢且可以通過swapCache()使用時(shí)觸發(fā) - cached
在應(yīng)用緩存完整可用時(shí)觸發(fā)
數(shù)據(jù)存儲
Cookie
最初是在客戶端用于存儲會話信息的星岗,服務(wù)器對任意HTTP請求發(fā)送Set-Cookie頭部,發(fā)送回服務(wù)器的額外信息可以用于唯一驗(yàn)證客戶來自于發(fā)送的那個(gè)請求首有。
- 限制
cookie在性質(zhì)上是綁定在特定的域名下的(瀏覽器限制)燕垃,每個(gè)域的cookie總數(shù)是有限的枢劝,當(dāng)超過數(shù)量時(shí),瀏覽器會清除以前設(shè)置的cookie卜壕,且會影響傳輸速度 - cookie的構(gòu)成
名稱:不區(qū)分大小寫
值:存儲在cookie中的字符串值您旁,值必須被URL編碼
域:cookie對于哪個(gè)域是有效的。所有向該域發(fā)送的請求都會包含這個(gè)cookie信息轴捎。這個(gè)只可以包含子域鹤盒,也可以不包含
路徑:對于指定域中的路徑,應(yīng)該向服務(wù)器發(fā)送cookie侦副,例如:
http://www.xxxx.com/demo/中才能訪問侦锯,那么http://www.xxxx.com的頁面就不會發(fā)送cookie信息,即使請求都是來自同一個(gè)域的秦驯。
失效時(shí)間:是GMT格式的日期尺碰,用于指定應(yīng)該刪除的準(zhǔn)確時(shí)間
安全標(biāo)志:指定后cookie只有在使用SSL連接的時(shí)候才發(fā)動到服務(wù)器。
HTTP/1.1 200 OK
Content-type: text/html
Set-Cookie: name=value;
domain=.wrox.com; path=/; secure
Other-header: other-header-value
//以上表示一個(gè)叫做name的cookie译隘,
//在格林威治2007年1月22日7:10:24失效亲桥,
//同時(shí)對于www.wrow.com和wrow.com的任何子域(如:p2p.wrow.com)都有效
//secure 是cookie中唯一一個(gè)非名值對的部分,直接包含一個(gè)secure單詞固耘。
域两曼、路徑、失效時(shí)間玻驻、和secure標(biāo)志都是讀武器給瀏覽器的知識,以指定何時(shí)應(yīng)該發(fā)送cookie偿枕,這些參數(shù)并不會作為發(fā)送到服務(wù)器的cookie信息的一部分璧瞬,只有名值對才會被發(fā)送
3.JavaScript的cookie
name1=value1;name2=value2;name3=value3
所有名字和值都是經(jīng)過URL編碼的,所以必須使用decodeURIComponent()來解碼
4.子cookie
為了繞開瀏覽器的但域名下的cookie數(shù)限制渐夸,一些開發(fā)人員使用了一種稱為子cookie嗤锉。例如:
name=name1=value1&name2=value2&name3=value3=name4&value4&name5=value5
這昂網(wǎng)站或者web應(yīng)用可以無需達(dá)到單域名cookie上限也可以存儲更加結(jié)構(gòu)化的數(shù)據(jù)。
5.關(guān)于cookie的思考
還有一類cookie被稱為“HTTP專有cookie”墓塌,HTTP專有cookie可以從瀏覽器或者服務(wù)器設(shè)置瘟忱,但是只能從服務(wù)端讀取,因?yàn)镴avaScript無法獲取HTTP專有的cookie值
不要在cookie中存儲敏感信息苫幢,因?yàn)槎伎梢员辉L問到
cookie信息越大访诱。完成對服務(wù)的請求時(shí)間也就越長。
Web存儲機(jī)制
Web Storage
- 提供一種在cookie之外存儲會話數(shù)據(jù)的途徑
- 提供一種存儲大量可以跨會話存在的數(shù)據(jù)的機(jī)制
- Storage類型
Storage類型提供最大的存儲空間韩肝,storage的實(shí)例于其他對象類似
clear():刪除所有值触菜,F(xiàn)irefox中沒有實(shí)現(xiàn),只能清除值哀峻,不建議使用這個(gè)方法涡相。
getItem(name): 根據(jù)指定的名字name獲取對應(yīng)的值
key(index):獲得index位置處的值的名字
removeItem(name):刪除由name指定的名值對
setItem(name,value):為指定的name設(shè)置一個(gè)對應(yīng)的值
Storage類型只能存儲字符串哲泊,非字符串的數(shù)據(jù)在存儲之前會被轉(zhuǎn)換成字符串
2.sessionStorage對象
sessionStorage對象存儲特定于某個(gè)會話的數(shù)據(jù),也就是該數(shù)據(jù)值保持到瀏覽器關(guān)閉催蝗。這個(gè)對象就像會話cookie切威,也會在瀏覽器關(guān)閉后消失,刷新仍然保留丙号。
3.globalStorage對象(已被localStorage取代)
4.localStorage對象
不能給localStorage指定任何訪問規(guī)則先朦,頁面必須來自同一個(gè)域名(子域名無效)使用同一種協(xié)議。
5.限制
localStorage:大多數(shù)是5MB槽袄,Chrome和Safari限制是2.5MB
sessionStorage:大多數(shù)是5MB烙无,Chrome和Safari限制是2.5MB
IndexedDB
Indexed Database API,或者簡稱為IndexedDB遍尺,是在瀏覽器中保存結(jié)構(gòu)化數(shù)據(jù)的一種數(shù)據(jù)庫截酷。
IndexedDB的思想是創(chuàng)建一套API,方便保存和讀取JavaScript對象乾戏,同時(shí)還支持查詢及搜索迂苛。
我們使用時(shí)還是應(yīng)該要注意瀏覽器的兼容
var indexedDB = window.indexedDB ||
window.msIndexedDB ||
window.mozIndexedDB ||
window.webkitIndexedBD;
數(shù)據(jù)庫
IndexedDB就是一個(gè)數(shù)據(jù)庫,可以被網(wǎng)頁腳本創(chuàng)建和操作鼓择,IndexDB允許存儲大量數(shù)據(jù)三幻,提供查找接口,還能建立索引呐能,與MySQL或Web SQL Database等這些你以前可能用過的數(shù)據(jù)庫類似念搬。IndexedDB最大的特色是使用對象保存數(shù)據(jù),而不是用表來保存摆出。(IndexDB不屬于關(guān)系型數(shù)據(jù)庫朗徊,不支持SQL查詢)
特性:
1.鍵值對存儲
IndexDB內(nèi)部采用對象倉庫(object store)存放數(shù)據(jù),所有類型的數(shù)據(jù)都可以直接存入偎漫,包括JavaScript對象爷恳,數(shù)據(jù)以“鍵值對”形式保存,每一額數(shù)據(jù)記錄都有對應(yīng)的主鍵象踊,主鍵獨(dú)一無二温亲,重復(fù)會報(bào)錯(cuò)。
2.異步
IndexedDB操作時(shí)不會鎖死瀏覽器杯矩,用戶依然可以進(jìn)行其他操作(LocalStorage是同步操作)
3.支持事務(wù)
IndexedDB支持事務(wù)(transaction 游標(biāo))栈虚,這意味著一系列操作步驟中,只要有一步失敗史隆,整個(gè)事務(wù)就都取消节芥,數(shù)據(jù)庫回滾到事務(wù)發(fā)生之前的狀態(tài),不存在只改寫一部分?jǐn)?shù)據(jù)的情況。
4.同源限制
IndexDB受到同源限制头镊,每一個(gè)數(shù)據(jù)庫對象創(chuàng)建它的域名蚣驼,網(wǎng)頁只能訪問自身域名下的數(shù)據(jù)庫,而不能訪問跨域的數(shù)據(jù)庫相艇。
5.存儲空間大
一般不少于250MB,甚至沒有上限
6.支持二進(jìn)制存儲
不僅可以存儲字符串颖杏,還可以存儲二進(jìn)制數(shù)據(jù)(ArrayBuffer對象和Blob對象)
操作
- 打開數(shù)據(jù)庫
indexDB.open()
如果存在則打開,如果不存在就創(chuàng)建一個(gè)并打開
調(diào)用indexDB.open()會返回一個(gè)IDBRequest對象坛芽,這個(gè)對象上可以添加onerror和onsuccess事件處理程序
var request, database
request = indexedDB.open('demo'); // window.indexedDB.open(databaseName,version)
request.onerror = function(event){
console.log('Something bad happened',event)
}
request.onsuccess = function (event){
console.log(event)
}
request.onupgradeneeded = function (event) {
console.log(event)
} //upgradeneeded事件留储,如果指定版本號大于數(shù)據(jù)庫的實(shí)際版本號,會發(fā)生數(shù)據(jù)庫升級事件
方法接受2個(gè)參數(shù)咙轩,第一個(gè)參數(shù)是數(shù)據(jù)庫的名字获讳,如果不存在新建,第二個(gè)參數(shù)是整數(shù)活喊,表示版本丐膝,如果省略,打開已有的數(shù)據(jù)庫時(shí)钾菊,默認(rèn)為當(dāng)前版本帅矗,新建數(shù)據(jù)庫時(shí),默認(rèn)為1
錯(cuò)誤碼
IDBDatabaseException.UNKNOWN_ERR(1):意外錯(cuò)誤煞烫,無法歸類浑此。
IDBDatabaseException.NON_TRANSIENT_ERR(2):操作不合法。
IDBDatabaseException_NOT_FOUND_ERR(3):未發(fā)現(xiàn)要操作的數(shù)據(jù)庫滞详。
IDBDatabaseException.CONSTRAINT_ERR(4):違反了數(shù)據(jù)庫約束凛俱。
IDBDatabaseException.DATA_ERR(5):提供給事務(wù)的數(shù)據(jù)不能滿足要求。
IDBDatabaseException.NOT_ALLOWED_ERR(6):操作不合法料饥。
IDBDatabaseException.TRANSACTION_INACTIVE_ERR(7):試圖重用已完成的事務(wù)最冰。
IDBDatabaseException.ABORT_ERR(8):請求中斷,未成功稀火。
IDBDatabaseException.READ_ONLY_ERR(9):試圖在只讀模式下寫入或修改數(shù)據(jù)。
IDBDatabaseException.TIMEOUT_ERR(10):在有效時(shí)間內(nèi)未完成操作赌朋。
IDBDatabaseException.QUOTA_ERR(11):磁盤空間不足凰狞。
默認(rèn)情況下,IndexedDB數(shù)據(jù)庫是沒有版本號的沛慢,最好一開始就為數(shù)據(jù)庫指定一個(gè)版本號赡若。為此可以調(diào)用setVersion()方法,傳入以字符串形式表示的版本號团甲。同樣逾冬,調(diào)用這個(gè)方法也會返回一個(gè)請求對象。
function openIndexedDB(){
return new Promise(
(resolve,reject) => {
var request;
request = indexedDB.open('demo');
request.onerror = function(event){
reject(event.target.errorCode)
}
request.onsuccess = function (event){
resolve(event.target.result)
}
}
)
}
openIndexedDB().then(
(res) => {
console.log('IndexedDB',res)
}
)
- 對象存儲空間
在建立了與數(shù)據(jù)庫的連接之后,下一步就是使用對象存儲空間身腻,如果數(shù)據(jù)庫的版本與你傳入的版本不匹配产还,需要?jiǎng)?chuàng)建一個(gè)新的對象存儲空間,在創(chuàng)建存儲空間之前嘀趟,必須要想清楚你想要什么數(shù)據(jù)類型脐区。
var user = {
username: '007',
firstName:'James',
lastName:'Bond',
password: 'foo',
}
在這里貼一篇詳細(xì)的IndexDB的文章
https://wangdoc.com/javascript/bom/indexeddb.html#indexeddb-%E5%AF%B9%E8%B1%A1
3.新建數(shù)據(jù)庫
新建數(shù)據(jù)庫與打開數(shù)據(jù)庫是同一個(gè)操作,不存在就新建她按,不同的在于牛隅,后續(xù)的操作主要在upgradeneeded事件的監(jiān)聽函數(shù)完成,因?yàn)榘姹緩臒o到有酌泰,就是觸發(fā)媒佣。
- 新建倉庫(同MySql里的新建表)
request.onupgradeneeded = function (event){
db = event.target.result;
var objectStore = db.createObjectStore('person',{ keyPath: 'id' });
}
- 新增表
數(shù)據(jù)庫新建成功后,新增一張叫做 person 的表格陵刹,主鍵是 id
更好的寫法是先判斷一下默伍,這張表格是否存在,不存在再新建
var db = null;
var request = window.indexedDB.open('demo')
request.onerror = function (event) {
db = request.result
console.log( '數(shù)據(jù)庫打開報(bào)錯(cuò)' )
}
request.onsuccess = function (event) {
db = request.result
console.log( '數(shù)據(jù)庫打開成功' )
}
request.onupgradeneeded = function (event) {
db = event.target.result
var objectStore ;
if(!db.objectStoreNames.contains('person')){
objectStore = db.createObjectStore('person', {keyPath: 'id'})
}
console.log('創(chuàng)建了倉庫',objectStore);
}
主鍵(keyPath)是默認(rèn)建立索引的屬性授霸,比如:數(shù)據(jù)記錄是{ id: 1, name: '張三' }巡验,那么id屬性可以作為主鍵。主鍵也可以指定為下一層對象的屬性碘耳,比如:{ foo, { bar: 'baz' } }的foo.bar也可以指定為主鍵显设。
如果 數(shù)據(jù)記錄里面沒有合適作為主鍵的屬性,那么可以讓IndexedDB自動生成主鍵
var objectStore = db.createObjectStore(
'person',
{ autoIncrement: true }
);
//指定主鍵為一個(gè)遞增的整數(shù)
- 新建索引
新建倉庫后辛辨,下一步可以新建索引
request.onupgradeneeded = function (event) {
db = event.target.result
var objectStore ;
if(!db.objectStoreNames.contains('person')){
objectStore = db.createObjectStore('person', {autoIncrement: true})
objectStore.createIndex( 'name', 'name', {unique: false} )
objectStore.createIndex( 'email', 'email', {unique: true} )
//createIndex的三個(gè)參數(shù)分別為索引名稱捕捂、索引所在的屬性、配置對象(說明該屬性是否包含重復(fù)的值)
}
console.log('創(chuàng)建了倉庫',objectStore);
}
事務(wù)
接下來的一系列我們都叫做事務(wù)斗搞。
在數(shù)據(jù)庫對象上調(diào)用transaction()方法可以創(chuàng)建事務(wù)指攒,任何時(shí)候,只要想讀取或修改數(shù)據(jù)僻焚,都要通過事務(wù)來組織所有操作允悦。
var transaction = db.transaction(name);
//name為需要傳入的參數(shù)
var transaction = db.transaction(['name','email']);
//如果需要訪問多個(gè)對象存儲空間,也可以在第一個(gè)參數(shù)的位置傳字符串?dāng)?shù)組
以上都是以只讀方式訪問數(shù)據(jù)虑啤,要修改訪問方式隙弛,必須在創(chuàng)建事務(wù)時(shí)傳入第二個(gè)參數(shù),這個(gè)參數(shù)表示訪問模式
READ_ONLY:只讀
READ_WRITE:讀寫
VERSION_CHANGE:改變
IE10+和Firefox 4+實(shí)現(xiàn)的是IDBTransaction狞山,但在Chrome中則叫webkitIDBTransaction全闷,使用以下代碼可以統(tǒng)一:
var IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction
var transaction = db.transaction('users',IDBTransaction.READ_WRITE)
- 新增數(shù)據(jù)
新增數(shù)據(jù)指的是向?qū)ο髠}庫寫入數(shù)據(jù)記錄。通過事務(wù)完成
function createStore (){
return new Promise(
(resolve,reject) => {
var db = null;
var request = window.indexedDB.open('demo')
request.onerror = function (event) {
db = request.result
console.log( '數(shù)據(jù)庫打開報(bào)錯(cuò)',db )
reject (db)
}
request.onsuccess = function (event) {
db = request.result
console.log( '數(shù)據(jù)庫打開成功',db )
resolve(db)
}
request.onupgradeneeded = function (event) {
db = event.target.result
var objectStore;
if (!db.objectStoreNames.contains('person')) {
objectStore = db.createObjectStore('person', {autoIncrement: true})
objectStore.createIndex('name', 'name', {unique: false})
objectStore.createIndex('email', 'email', {unique: true})
//createIndex的三個(gè)參數(shù)分別為索引名稱萍启、索引所在的屬性总珠、配置對象(說明該屬性是否包含重復(fù)的值)
}
console.log('創(chuàng)建了倉庫', objectStore);
// return db
}
}
)
}
async function add() {
let db = await createStore()
var request = db.transaction(['person'], 'readwrite')
.objectStore('person')
.add({ id: 1, name: '張三', age: 24, email: 'zhangsan@example.com' });
request.onsuccess = function (event) {
console.log('數(shù)據(jù)寫入成功');
};
request.onerror = function (event) {
console.log('數(shù)據(jù)寫入失敗', event);
}
}
add()
- 讀取數(shù)據(jù)
- 遍歷數(shù)據(jù)
- 更新數(shù)據(jù)
- 刪除數(shù)據(jù)
- 使用索引