數據庫是屬於服務器的,這是天經地義的事,但是有時候數據也許並非需要存儲在服務器,但是這些數據也是一條一條的記錄,怎么辦?今天來帶領你領略一下H5的一個新特性--indexedDB的風騷。你會情不自禁的發出感嘆--不可思議!
一、鏈接數據庫
indexedDB沒有創建數據庫的概念,你可以直接鏈接數據庫,如果你鏈接的數據庫並不存在,那么會自動的創建一個數據庫。看下面的這個例子。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <button type="" id='conbtn'>鏈接數據庫</button> <script> // In the following line, you should include the prefixes of implementations you want to test. window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB; // DON'T use "var indexedDB = ..." if you're not in a function. // Moreover, you may need references to some window.IDB* objects: window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction; window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange // (Mozilla has never prefixed these objects, so we don't need window.mozIDB*) function connectDB(name,version,success,error){ let dbConnect = indexedDB.open(name,version); dbConnect.onsuccess = function(e){ console.log('數據庫鏈接成功!'); success(e.target.result); } dbConnect.onerror = function(e){ console.log('數據庫鏈接失敗!'); error(e); } dbConnect.onupgradeneeded = function(e){ success(e.target.result); let oldVersion = e.oldVersion; let newVersion = e.newVersion; console.log('數據庫更新成功,舊的版本號為:'+oldVersion+",新的版本號為:"+newVersion); } } window.onload=function(){ let btn = document.getElementById('conbtn'); btn.onclick = function(){ connectDB('haha',1,function(){ console.log('鏈接成功,或者更新成功回調函數'); },function(){ console.log('鏈接失敗回調函數!') }); } } </script> </body> </html>
我點了兩次鏈接數據庫,結果是這樣的。
在Application的indexedDB中我們發現多了一個東西。
可以看到haha數據庫已經成功建立了。
indexedDB的open方法用來鏈接或者更新數據庫,第一次創建也認為是一次更新。第一個參數是數據庫的名字,第二個參數為版本號。第一次創建或者版本號發生改變時出發更新事件upgradeneeded,鏈接成功后出發success事件,鏈接出錯時觸發error事件。
二、建表和索引
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <button type="" id='conbtn'>鏈接數據庫</button> <script> // In the following line, you should include the prefixes of implementations you want to test. window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB; // DON'T use "var indexedDB = ..." if you're not in a function. // Moreover, you may need references to some window.IDB* objects: window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction; window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange // (Mozilla has never prefixed these objects, so we don't need window.mozIDB*) function connectDB(name,version,success,update,error){ let dbConnect = indexedDB.open(name,version); dbConnect.onsuccess = function(e){ console.log('數據庫鏈接成功!'); success(e.target.result); } dbConnect.onerror = function(e){ console.log('數據庫鏈接失敗!'); error(e); } dbConnect.onupgradeneeded = function(e){ update(e.target.result); let oldVersion = e.oldVersion; let newVersion = e.newVersion; console.log('數據庫更新成功,舊的版本號為:'+oldVersion+",新的版本號為:"+newVersion); } } function createTable(idb,storeName,key,idxs){ if(!idb){ console.log(idb); return ; } if(!key || !idxs){ console.log('參數錯誤'); return ; } if(!storeName){ console.log('storeName必須'); return ; } try{ var store = idb.createObjectStore(storeName,key); console.log('數據庫存儲對象(table)創建成功'); for(var i = 0;i<idxs.length;i++){ var idx = idxs[i]; store.createIndex(idx.indexName,idx.keyPath,idx.optionalParameters); console.log('索引'+idx.indexName+'創建成功'); } }catch(e){ console.log('建表出現錯誤'); console.log(JSON.stringify(e)) } } window.onload=function(){ let btn = document.getElementById('conbtn'); btn.onclick = function(){ connectDB('haha',1, function(idb){ console.log('鏈接成功,或者更新成功回調函數'); },function(idb){ createTable(idb,'test',{keyPath:'id',autoIncrement:true},[ { indexName:'testIndex', keyPath:'name', optionalParameters:{ unique:true, multiEntry:false }}]); },function(){ console.log('鏈接失敗回調函數!') }); } } </script> </body> </html>
我點了一下按鈕結果時這樣的。
這里用到的兩個核心方法時createObjectStore,和createIndex,這兩個方法必須在數據庫發生更新的事件中執行。
createObjectStore方法可以理解成是創建表,接受第一個參數為一個字符串,表示表名,第二個參數是一個對象,指定主鍵相關的東西,{keyPath:'主鍵是哪個字段',autoIncrement:是否自增}。
createIndex方法是創建索引的,接受三個參數,第一個是一個字符串,表示索引的名字,第二個參數表示字段名(誰的索引),第三個參數是個對象,具體自己查去。索引的作用是在查詢操作時可以用到,不詳講,自行google吧。
三、添加數據
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <button type="" id='conbtn'>鏈接數據庫</button> <button type="" id='add'>添加數據</button> <script> // In the following line, you should include the prefixes of implementations you want to test. window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB; // DON'T use "var indexedDB = ..." if you're not in a function. // Moreover, you may need references to some window.IDB* objects: window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction; window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange // (Mozilla has never prefixed these objects, so we don't need window.mozIDB*) function connectDB(name,version,success,update,error){ let dbConnect = indexedDB.open(name,version); dbConnect.onsuccess = function(e){ console.log('數據庫鏈接成功!'); success(e.target.result); } dbConnect.onerror = function(e){ console.log('數據庫鏈接失敗!'); error(e); } dbConnect.onupgradeneeded = function(e){ update(e.target.result); let oldVersion = e.oldVersion; let newVersion = e.newVersion; console.log('數據庫更新成功,舊的版本號為:'+oldVersion+",新的版本號為:"+newVersion); } } function createTable(idb,storeName,key,idxs){ if(!idb){ console.log(idb); return ; } if(!key || !idxs){ console.log('參數錯誤'); return ; } if(!storeName){ console.log('storeName必須'); return ; } try{ var store = idb.createObjectStore(storeName,key); console.log('數據庫存儲對象(table)創建成功'); for(var i = 0;i<idxs.length;i++){ var idx = idxs[i]; store.createIndex(idx.indexName,idx.keyPath,idx.optionalParameters); console.log('索引'+idx.indexName+'創建成功'); } }catch(e){ console.log('建表出現錯誤'); console.log(JSON.stringify(e)) } } function add(storeName,values){ connectDB('haha',1,function(idb){ var ts = idb.transaction(storeName,"readwrite"); var store = ts.objectStore(storeName); for(var i = 0;i<values.length;i++){ (function(i){ var value = values[i]; var req = store.put(value); req.onsuccess = function(){ console.log("添加第"+i+"個數據成功"); } req.onerror = function(e){ console.log("添加第"+i+"個數據失敗"); console.log(JSON.stringify(e)); } })(i) } ts.oncomplete = function(){ console.log('添加數據事務結束!'); } },function(){},function(){}); } window.onload=function(){ let btn = document.getElementById('conbtn'); btn.onclick = function(){ connectDB('haha',1, function(idb){ console.log('鏈接成功,或者更新成功回調函數'); },function(idb){ createTable(idb,'test',{keyPath:'id',autoIncrement:true},[ { indexName:'testIndex', keyPath:'name', optionalParameters:{ unique:true, multiEntry:false }}]); },function(){ console.log('鏈接失敗回調函數!') }); } let add1 = document.getElementById('add'); add1.onclick = function(){ add('test',[{name:"fjidfji",a:'uuuu'}]) } } </script> </body> </html>
比較神奇的是你在建表的時候不需要指定你的字段,再添加數據時可以隨便加。添加數據用到的是表對象的put方法,之前需要一個事務,從事務調個方法拿到存儲對象(可以理解為表),具體看看例子就明白了。
四、查詢數據
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <button type="" id='conbtn'>鏈接數據庫</button> <button type="" id='add'>添加數據</button> <button type="" id="selectBtn">查詢</button> <script> // In the following line, you should include the prefixes of implementations you want to test. window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB; // DON'T use "var indexedDB = ..." if you're not in a function. // Moreover, you may need references to some window.IDB* objects: window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction; window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange // (Mozilla has never prefixed these objects, so we don't need window.mozIDB*) function connectDB(name,version,success,update,error){ let dbConnect = indexedDB.open(name,version); dbConnect.onsuccess = function(e){ console.log('數據庫鏈接成功!'); success(e.target.result); } dbConnect.onerror = function(e){ console.log('數據庫鏈接失敗!'); error(e); } dbConnect.onupgradeneeded = function(e){ update(e.target.result); let oldVersion = e.oldVersion; let newVersion = e.newVersion; console.log('數據庫更新成功,舊的版本號為:'+oldVersion+",新的版本號為:"+newVersion); } } function createTable(idb,storeName,key,idxs){ if(!idb){ console.log(idb); return ; } if(!key || !idxs){ console.log('參數錯誤'); return ; } if(!storeName){ console.log('storeName必須'); return ; } try{ var store = idb.createObjectStore(storeName,key); console.log('數據庫存儲對象(table)創建成功'); for(var i = 0;i<idxs.length;i++){ var idx = idxs[i]; store.createIndex(idx.indexName,idx.keyPath,idx.optionalParameters); console.log('索引'+idx.indexName+'創建成功'); } }catch(e){ console.log('建表出現錯誤'); console.log(JSON.stringify(e)) } } function add(storeName,values){ connectDB('haha',1,function(idb){ var ts = idb.transaction(storeName,"readwrite"); var store = ts.objectStore(storeName); for(var i = 0;i<values.length;i++){ (function(i){ var value = values[i]; var req = store.put(value); req.onsuccess = function(){ console.log("添加第"+i+"個數據成功"); } req.onerror = function(e){ console.log("添加第"+i+"個數據失敗"); console.log(JSON.stringify(e)); } })(i) } ts.oncomplete = function(){ console.log('添加數據事務結束!'); } },function(){},function(){}); } function select(storeName,count,callback){ connectDB('haha',1,function(idb){ var total = 0; var data = []; var ts = idb.transaction(storeName,"readonly"); var store = ts.objectStore(storeName); var req = store.count(); var req2 = null; req.onsuccess = function(){ total = this.result; var realCount = (count<=total)?count:total; var range = IDBKeyRange.bound(0,Number.MAX_VALUE); req2 = store.openCursor(range,'prev'); var cc = 0; req2.onsuccess = function(){ var cursor = this.result; if(cursor){ cc++; data.push(cursor.value); if(cc==realCount){ callback(data); return; } cursor.continue(); } } req2.onerror = function(){ console.log("檢索出錯") } } },function(){},function(){}); } window.onload=function(){ let btn = document.getElementById('conbtn'); btn.onclick = function(){ connectDB('haha',1, function(idb){ console.log('鏈接成功,或者更新成功回調函數'); },function(idb){ createTable(idb,'test',{keyPath:'id',autoIncrement:true},[ { indexName:'testIndex', keyPath:'name', optionalParameters:{ unique:true, multiEntry:false }}]); },function(){ console.log('鏈接失敗回調函數!') }); } let add1 = document.getElementById('add'); add1.onclick = function(){ add('test',[{name:"fjidfji",a:'uuuu'}]) } let selectBtn = document.getElementById('selectBtn'); selectBtn.onclick = function(){ select('test',2,function(data){ console.log(data); }) } } </script> </body> </html>
查詢操作主要用到了游標,這個說起來還比較多,沒時間說了,自行google。還有很多的操作這里不講了。給一個我封裝的不是很好的簡易庫,僅供參考。
一個簡易庫。
(function(window){ 'use strict'; window.dbUtil={ indexedDB :(window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB), IDBTransaction :(window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction), IDBKeyRange :(window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange), IDBCursor : (window.IDBCursor || window.webkitIDBCursor || window.msIDBCursor), idb:null, dbName:"", dbVersion:"", /** * 初始化數據庫,創建表和建立鏈接 * @param {[type]} dbName [description] * @param {[type]} dbVersion [description] * @param {[type]} storeObjs [description] * @return {[type]} [description] */ initDB:function (dbName,dbVersion,storeObjs){ this.dbName = dbName; this.dbVersion = dbVersion; var dbConnect = this.indexedDB.open(this.dbName,this.dbVersion); var self = this; dbConnect.onsuccess = function(e){ self.idb = e.target.result; self.log('數據庫鏈接成功!'); } dbConnect.onerror = function(e){ console.log(e) self.log('數據庫鏈接失敗!'); } dbConnect.onupgradeneeded = function(e){ self.idb = e.target.result; var ts = e.target.transaction; var oldVersion = e.oldVersion; var newVersion = e.newVersion; self.log('數據庫更新成功,舊的版本號為:'+oldVersion+",新的版本號為:"+newVersion); if(storeObjs){ for(var k = 0,len=storeObjs.length;k<len;k++){ var storeObj = storeObjs[k]; var storeName = storeObj.storeName; var key = storeObj.key; var idxs = storeObj.idxs; self.createTable(storeName,key,idxs) } } } }, /** * 創建數據庫存儲對象(表) * @param {[type]} key [description] * @param {[type]} [description] * @return {[type]} [description] */ createTable:function(storeName,key,idxs){ var self = this; var idb = self.idb; if(!idb){ self.log('數據庫沒有鏈接'); return ; } if(!key || !idxs){ self.log('參數錯誤'); return ; } if(!storeName){ self.log('storeName必須'); return ; } try{ var store = idb.createObjectStore(storeName,key); self.log('數據庫存儲對象(table)創建成功'); for(var i = 0;i<idxs.length;i++){ var idx = idxs[i]; store.createIndex(idx.indexName,idx.keyPath,idx.optionalParameters); self.log('索引'+idx.indexName+'創建成功'); } }catch(e){ self.log('建表出現錯誤'); console.log(JSON.stringify(e)) } }, /** * [add description] * @param {[type]} storeName [description] * @param {[type]} values [description] */ add:function(storeName,values){ var dbConnect = this.indexedDB.open(this.dbName,this.dbVersion); var self = this; dbConnect.onsuccess = function(e){ var idb = e.target.result; var ts = idb.transaction(storeName,"readwrite"); var store = ts.objectStore(storeName); for(var i = 0;i<values.length;i++){ (function(i){ var value = values[i]; var req = store.put(value); req.onsuccess = function(){ self.log("添加第"+i+"個數據成功"); } req.onerror = function(e){ self.log("添加第"+i+"個數據失敗"); self.log(JSON.stringify(e)); } })(i) } ts.oncomplete = function(){ self.log('添加數據事務結束!'); } } }, /** * [select description] * @param {[type]} storeName [description] * @param {[type]} count [description] * @param {Function} callback [description] * @param {[type]} indexName [description] * @return {[type]} [description] */ select:function(storeName,count,callback,indexName){ var dbConnect = this.indexedDB.open(this.dbName,this.dbVersion); var self = this; var total = 0; var data = []; dbConnect.onsuccess = function(e){ self.log("數據庫鏈接成功!"); var idb = e.target.result; var ts = idb.transaction(storeName,"readonly"); var store = ts.objectStore(storeName); var req = store.count(); var req2 = null; req.onsuccess = function(){ total = this.result; var realCount = (count<=total)?count:total; if(typeof indexName == 'undefined'){ var range = IDBKeyRange.bound(0,"9999999999999999999999"); req2 = store.openCursor(range,'prev'); var cc = 0; req2.onsuccess = function(){ var cursor = this.result; if(total == 0){ callback([]); return; } if(cursor){ cc++; data.push(cursor.value); if(cc==realCount){ callback(data); return; } cursor.continue(); } } req2.onerror = function(){ self.log("檢索出錯") } }else{ //待寫 } } } }, /** * [delete description] * @param {[type]} storeName [description] * @param {[type]} key [description] * @return {[type]} [description] */ delete:function(storeName,key,callback){ var dbConnect = this.indexedDB.open(this.dbName,this.dbVersion); let self = this; dbConnect.onsuccess = function(e){ var idb = e.target.result; var ts = idb.transaction(storeName,'readwrite'); var store = ts.objectStore(storeName); store.delete(key); self.log('刪除成功!'); if(callback){ callback(); } } }, /** * [funciton description] * @param {[type]} storeName [description] * @param {[type]} key [description] * @param {[type]} existCall [description] * @param {[type]} notExistCall [description] * @return {[type]} [description] */ isExist:function(storeName,key,existCall,notExistCall){ var dbConnect = this.indexedDB.open(this.dbName,this.dbVersion); dbConnect.onsuccess = function(e){ var idb = e.target.result; var ts = idb.transaction(storeName,'readonly'); var store = ts.objectStore(storeName); var req = store.get(key); req.onsuccess = function(){ if(this.result == undefined){ notExistCall(); }else{ existCall(this.result); } } req.onerror = function(){ notExistCall(); } } }, /** * clear * @param {[type]} storeName [description] * @return {[type]} [description] */ clear:function clearObjectStore(storeName){ var dbConnect = this.indexedDB.open(this.dbName,this.dbVersion); dbConnect.onsuccess = function(e){ var idb = e.target.result; var ts = idb.transaction(storeName,'readwrite'); var store = ts.objectStore(storeName); store.clear(); } }, /** * 刪除數據庫 * @param {[type]} dbName [description] * @return {[type]} [description] */ dropDatabase:function(dbName){ this.indexedDB.deleteDatabase(dbName); this.log('成功刪除數據庫:'+dbName); }, /** * [log description] * @param {[type]} msg [description] * @return {[type]} [description] */ log:function(msg){ console.log((new Date()).toTimeString()+":"+msg) } } })(window);
五、使用indexedDB的優缺點
1、優點:可以將一些數據放到瀏覽器端,不用和服務器交互就可以實現一些功能,減輕服務器負擔,加快響應速度。
2、缺點:
(1)不可靠:用戶可能刪處indexedDB,而且更換瀏覽器或者設備后這些數據就沒了。
2)不便於數據采集:有很多時候將數據存到服務器是為了獲得一些數據,如果放到瀏覽器端,這些數據比較難獲取。
(3)無法共享:不同用戶沒辦法共享數據,甚至時一個設備的不同瀏覽器也沒法共享。
所以,是否適用indexedDB要進行一下利弊權衡,不是有了indexedDB就啥也不管,一骨腦將數據放進去。
最近兩個課程設計,還有面試, 文章寫的比較匆忙,如有問題請各位園友批評指正。最近找實習,各位園友要是我寫的東西還可以而且公司招聘實習生的話可以給大熊一個機會,謝謝!