建立與使用Indexed DB

by vivid 27. 三月 2013 11:47

.NET Magazine國際中文電子雜誌
作 者:許薰尹
審 稿:張智凱
文章編號:N130313402
出刊日期:2013/3/27

雖然HTML5定義Web Storage API,可以在用戶端利用sessionStorage或localStoreage儲存資料,但當儲存資料的結構較複雜時,就不太容易使用。Indexed Database API,簡稱 IndexedDB,則提供一個有效的方式儲存、取回或搜尋較複雜的結構化資料。

你可以利用window物件的indexedDB屬性來存取IndexedDB。若瀏覽器是IE,您必需設定允許使用瀏覽器快取以及資料庫。以IE10瀏覽器為例,可於「工具」-「網際網路選項」-「一般」-「設定」-「快取與資料庫」,勾選「允許網站快取與資料庫」,請參考下圖所示:

clip_image002

圖 1:IE10須勾選「允許網站快取與資料庫」才可使用IndexedDB。

 

使用modernizr判斷支援度

另外在程式之中,我們可以使用modernizr來判斷是否支援IndexedDB,例如以下程式範例,引用最新版的modernizr-latest.js函式庫,只要Modernizr.indexeddb語法回傳true,就代表瀏覽器支援IndexedDB:

 

<!DOCTYPE html>
<html xmlns = "http://www.w3.org/1999/xhtml">
<head>
  <title> </title>
  <script type = "text/javascript" src = "http://modernizr.com/downloads/modernizr-latest.js"> </script>
  <script type = "text/javascript">
    function contentLoaded() {
      if ( Modernizr.indexeddb )
        alert( "support" );
      else
        alert( "Not support" );
    };
    window.addEventListener( "DOMContentLoaded", contentLoaded, false );
  </script>
</head>
<body>
</body>
</html>

 

 

使用indexedDB屬性判斷支援度

另一種判斷的方式就是檢視window物件的indexedDB屬性,因為IndexedDB規格尚未完全確定,因此需要為不同的瀏覽器使用廠商專屬的屬性,window.webkitIndexedDB 是給Chrome瀏覽器使用的,而mozIndexedDB則是給Firefox;msIndexedDB則是給IE使用的,參考以下範例程式碼:

<!DOCTYPE html>
<html xmlns = "http://www.w3.org/1999/xhtml">
<head>
  <title> </title>
  <script type = "text/javascript">
      var indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB;

    function contentLoaded () {
      if ( indexedDB )
        alert( "support" );
      else
        alert( "Not support" );
    };
    window.addEventListener( "DOMContentLoaded", contentLoaded, false );
  </script>
</head>
<body>
</body>
</html>


 

開啟IndexedDB資料庫

要開啟IndexedDB資料庫,可以利用叫用indexedDB的open()方法,此方法會回傳一個 request物件,若資料庫不存在,open()方法將會建立資料庫。這個建立資料庫的動作是非同步的,因此你可以接聽request物件的onsuccess事件,以做後續處理;若資料庫開啟錯誤,則可接聽request物件的onerror事件,參考以下範例程式碼:

<!DOCTYPE html>
<html xmlns = "http://www.w3.org/1999/xhtml">
<head>
  <title> </title>
  <script type = "text/javascript">
    var indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB;
    var db;

    function contentLoaded( ) {
      var openRequest = indexedDB.open( "EmployeeDB" );
      openRequest.onsuccess = function ( event ) {
        db = event.target.result;
        alert( "Open DB" );
      };

      openRequest.onerror = function ( event ) {
        alert( "發生錯誤 : " + event.target.errorCode );
      };
    };
    window.addEventListener( "DOMContentLoaded", contentLoaded, false );
  </script>
</head>
<body>
</body>
</html>


若想要知道目前用戶端有多少個IndexedDB,可以使用Chrome瀏覽器提供的視覺化介面,請參考下圖所示,開啟Chrome瀏覽器(目前版本為25)的開發人員工具,從「Resources」頁面中可以看到IndexedDB項目:

clip_image004

圖 2:使用Chrome瀏覽器提供的視覺化介面檢視IndexedDB。

 

初始化資料庫

一個IndexedDB資料庫中可以有多個Object Store,您可以把Object Store當做是資料庫的資料表(table)。每回叫用open()方法開啟資料庫時,可以在方法第二個參數設定資料庫版本號碼,例如開啟第二版資料庫(預設建立資料庫時,第一個版本版號為1):

 

var openRequest = indexedDB.open( "EmployeeDB" , 2 );

當資料庫不存在,或欲開啟的資料庫版本和目前使用的不同時,便會觸發onupgradeneeded事件。此事件會比資料庫Open()方法觸發的onsuccess事件還要早發生。因此資料庫第一次建立時,您可以在onupgradeneeded事件撰寫初始化資料表資料的程式碼。參考以下範例程式碼:

 

openRequest.onupgradeneeded = function ( event ) {
        //initialize  db & data       
        var store = event.target.result.createObjectStore( "Employees", { keyPath: "id" } );
        var employee1 = {
          id : 1,
          name : "Mary",
          email : "Mary@test.com",
          age : 30
        };
        store.add( employee1 );

        var employee2 = {
          id : 2,
          name : "Candy",
          email : "Candy@test.com",
          age : 44
        };
        store.add( employee2 );

      }


我們利用createObjectStore()方法來定義object Store。createObjectStore()方法的第一個參數為object store的名稱;第二個參數是一個選擇性參數,其中keyPath屬性設定key值為「id」。接著利用add方法新增資料到object store。

此範例利用Chrome瀏覽器測試執行,從開發人員工具中,可以看到新增到object store的資料,請參考下圖所示:

clip_image006

圖 3:檢視IndexedDB中的資料。

如前文所提,由於onupgradeneeded事件只會在資料庫不存在,或欲開啟的資料庫版本和目前使用的不同時觸發,因此下回執行此網頁時,並不會觸發此事件。

 

查詢Object Store

若要從Object Store將其中的資料一筆、一筆讀取出來,可以使用cursor。你可以透過transaction物件取得Object Store之後,叫用openCursor()方法。因其以非同步方式處理,您可以註冊onsuccess事件,從event.target.result屬性中取得cursor。

此事件觸發時,只會回傳第一個物件(即Object Store中的第一筆資料),後續可叫用continue方法重新觸發此事件一次,以取得下一筆。若event.target.result為null表代表沒有資料了。參考以下範例所示,利用cursor將讀取出的資料,顯示在清單之中:

<!DOCTYPE html>
<html xmlns = "http://www.w3.org/1999/xhtml">
<head>
  <title> </title>
  <script type = "text/javascript">
    var indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB;
    var db;
    var IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction;

    function contentLoaded( ) {
      var openRequest = indexedDB.open( "EmployeeDB" );

      openRequest.onupgradeneeded = function ( event ) {
        //initialize  db & data
        var store = event.target.result.createObjectStore( "Employees", { keyPath: "id" } );
        var employee1 = {
          id : 1,
          name : "Mary",
          email : "Mary@test.com",
          age : 30
        };

        store.add( employee1 );

        var employee2 = {
          id : 2,
          name : "Candy",
          email : "Candy@test.com",
          age : 44
        };
        store.add( employee2 );
      }

      openRequest.onsuccess = function ( event ) {
        db = event.target.result;
      };

      openRequest.onerror = function ( event ) {
        alert( "發生錯誤 : " + event.target.errorCode );
      };

      var getList = document.getElementById("getList");
      getList.addEventListener( "click", function ( event ) {

        //clear all child
        var eList = document.getElementById("eList");
        while ( eList.hasChildNodes() ) {
          eList.removeChild( eList.lastChild );
        }

        var transaction = db.transaction( "Employees", 'readwrite');
        var store = transaction.objectStore("Employees");

        var request = store.openCursor();
        request.onsuccess = function ( event ) {
          var cursor = event.target.result;

          if ( cursor ) {
            var li = cursor.key + " , " + cursor.value.name + " , " + cursor.value.email + " , " + cursor.value.age;
            var node = document.createElement("li");
            node.innerHTML= li;
            eList.appendChild( node );

            cursor.continue();
          }
        };

      })
    };
    window.addEventListener( "DOMContentLoaded", contentLoaded, false );
  </script>
</head>
<body>

  <button id = "getList"> 員工清單 </button> <br />
  員工清單
  <ul id = "eList"></ul>

</body>
</html>

 

此範例在IE10瀏覽器的執行結果,請參考下圖所示:

clip_image007

圖 4:IE10瀏覽器的執行結果。

最後提醒,此範例建立transaction物件時,傳遞「readwrite」字串來設定開啟object store的模式:

 

var transaction = db.transaction("Employees", 'readwrite' );

有些瀏覽器,如Firefox,可以使用列舉常值,例如將程式碼改為:

 

var transaction = db.transaction("Employees ", IDBTransaction.READ_WRITE );

但要小心,有些瀏覽器,如ie10目前還不支援列舉常值的寫法。

 

讀取object store資料中的一筆資料

你可以使用Object store的get()方法指定想要搜尋的物件之key值,從IndexedDB資料庫的Object Store中找尋資料。先透過transaction物件取得Object Store之後,叫用get()方法,再註冊onsuccess事件來取得資料,參考以下範例。

 

var btnGet = document.getElementById("btnGet");
    btnGet.addEventListener( "click", function ( event ) {
      var id = parseInt( document.getElementById("idToGet").value );
      var transaction = db.transaction("Employees", 'readwrite' );
      var store = transaction.objectStore("Employees");
      var getRequest = store.get( id );
      getRequest.onsuccess = function ( event ) {
        emp = event.target.result;
        alert("編號 : " + emp.id + ", 名字 : " + emp.name + " , EMail : " + emp.email + " , age : " + emp.age);
      }
      getRequest.onerror = function ( event ) {
        alert( "查詢發生錯誤" + event.target.errorCode );
      };
    });

 

刪除object store資料

我們可以使用delete()方法從Object Store刪除資料,例如以下範例,先透過transaction物件取得Object Store之後,叫用delete()方法傳入要刪除的物件之key值,並攔截onsuccess與onerror事件。

 

var btnDelete = document.getElementById("btnDelete");
btnDelete.addEventListener( "click", function ( event ) {
  var id = parseInt( document.getElementById("idToGet").value );
  var transaction = db.transaction("Employees", 'readwrite');
  var store = transaction.objectStore("Employees");
  var deleteRequest = store.delete( id );
  deleteRequest.onsuccess = function ( event ) {
    alert("已刪除!");
  }
  deleteRequest.onerror = function ( event ) {
    alert( "刪除發生錯誤" + event.target.errorCode );
  };
});

 

修改object store資料

若要修改object store中的資料可以利用objectStore的put()方法修改資料,先透過transaction物件取得Object Store之後,叫用put()方法,再註冊onsuccess事件與onerror事件來判斷修改的動作是法成功或失敗。

參考以下範例程式,建立一個employee2物件,並設定屬性值,根據這些值修改object store中key值為的資料。

var btnUpdate = document.getElementById("btnUpdate");
btnUpdate.addEventListener( "click", function () {
  var transaction = db.transaction( "Employees", 'readwrite' );
  var objectStore = transaction.objectStore("Employees");

  var employee2 = {
    id : 2,
    name : "new Candy",
    email : "Candy@aaa.com",
    age : 55
  };

  var request = objectStore.put( employee2 );
  request.onsuccess = function ( event ) {
    alert( "成功更新!" );
  }
  request.onerror = function ( event ) {
    alert( "更新失敗!" );
  };
});

 

資料異動完整程式

以下是前述的新增、刪除、修改、查詢IndexedDB object store資料的完整程式碼:

 

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <title></title>
  <script type="text/javascript">
    var indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB;
    var db;
    var IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction;

    function contentLoaded( ) {
      var openRequest = indexedDB.open("EmployeeDB");

      openRequest.onupgradeneeded = function ( event ) {
        //initialize  db & data
        var store = event.target.result.createObjectStore( "Employees", { keyPath: "id" } );
        var employee1 = {
          id : 1,
          name : "Mary",
          email : "Mary@test.com",
          age : 30
        };


        store.add( employee1 );

        var employee2 = {
          id : 2,
          name : "Candy",
          email : "Candy@test.com",
          age : 44
        };
        store.add( employee2 );
      }

      openRequest.onsuccess = function ( event ) {
        db = event.target.result;
        // alert("Open DB");
      };

      openRequest.onerror = function ( event ) {
        alert ("發生錯誤 : " + event.target.errorCode );
      };

      var getList = document.getElementById("getList");
      getList.addEventListener( "click", function ( event ) {

        //clear all child
        var eList = document.getElementById("eList");
        while ( eList.hasChildNodes() ) {
          eList.removeChild( eList.lastChild );
        }

        var transaction = db.transaction( "Employees", 'readwrite' );
        var store = transaction.objectStore("Employees");

        var request = store.openCursor();
        request.onsuccess = function ( event ) {
          var cursor = event.target.result;
          if (cursor) {

            var li = cursor.key + " , " + cursor.value.name + " , " + cursor.value.email + " , " + cursor.value.age;

            var node = document.createElement("li");
            eList.appendChild( node );
            node.innerHTML = li;
            cursor.continue();
          }
        };


      })

      //get a employee     
      var btnGet = document.getElementById("btnGet");
      btnGet.addEventListener( "click", function ( event ) {
        var id = parseInt( document.getElementById("idToGet").value );
        var transaction = db.transaction("Employees", 'readwrite');
        var store = transaction.objectStore("Employees");
        var getRequest = store.get( id );
        getRequest.onsuccess = function ( event ) {
          emp = event.target.result;
          alert("編號 : " + emp.id + ", 名字 : " + emp.name + " , EMail : " + emp.email + " , age : " + emp.age);
        }
        getRequest.onerror = function ( event ) {
          alert( "查詢發生錯誤" + event.target.errorCode );
        };
      });


      //delete a employee     
      var btnDelete = document.getElementById("btnDelete");
      btnDelete.addEventListener( "click", function ( event ) {
        var id = parseInt( document.getElementById("idToGet").value );
        var transaction = db.transaction("Employees", 'readwrite');
        var store = transaction.objectStore("Employees");
        var deleteRequest = store.delete( id );
        deleteRequest.onsuccess = function ( event ) {
          emp = event.target.result;
          alert( "已刪除!" );
        }
        deleteRequest.onerror = function (event) {
          alert( "刪除發生錯誤" + event.target.errorCode );
        };
      });


      //Modified
      var btnUpdate = document.getElementById("btnUpdate");
      btnUpdate.addEventListener( "click", function () {
        var transaction = db.transaction("Employees", 'readwrite');
        var objectStore = transaction.objectStore("Employees");

        var employee2 = {
          id : 2,
          name : "new Candy",
          email : "Candy@aaa.com",
          age : 55
        };

        var request = objectStore.put( employee2 );
        request.onsuccess = function ( event ) {
          alert( "成功更新!" );
        }
        request.onerror = function ( event ) {
          alert( "更新失敗!" );
        };
      });
    };
    window.addEventListener( "DOMContentLoaded", contentLoaded, false );
  </script>
</head>
<body>
  <div id = "result">
    <button id = "getList">員工清單</button>
    員工清單
     <ul id = "eList"></ul>
  </div>
  <input type = "text" id = "idToGet" value = "1" />
  <button id = "btnGet"> 取得員工資料 </button>
  <button id = "btnDelete"> 刪除員工資料 </button>
  <button id = "btnUpdate"> 修改員工資料 </button>
</body>
</html>

 

刪除Object Store所有資料

最後探討一下,如何刪除Object Store中的所有資料。可以利用transaction取得Object Store,透過objectStore的Clear()方法就可以刪除Object Store中的所有資料,參考以下範例程式碼所示:

var transaction = db.transaction( ["Employees"], "readwrite" );
        var objectStore = transaction.objectStore("Employees");
        var resetRequest = objectStore.clear();
        resetRequest.onsuccess = function ( event ) {
          alert( "clear success!" );
        }

Tags:

.NET Magazine國際中文電子雜誌 | HTML | 許薰尹Vivid Hsu

新增評論




  Country flag
biuquote
  • 評論
  • 線上預覽
Loading






NET Magazine國際中文電子雜誌

NET Magazine國際中文電子版雜誌,由恆逸資訊創立於2000,自發刊日起迄今已發行超過500篇.NET相關技術文章,擁有超過40000名註冊讀者群。NET Magazine國際中文電子版雜誌希望藉於電子雜誌與NET Developer達到共同學習與技術新知分享,歡迎每一位對.NET 技術有興趣的朋友們多多支持本雜誌,讓作者群們可以有持續性的動力繼續爬文。<請加入免費訂閱>

月分類Month List