客户端数据存储

对于 Web Application 来说,客户端数据存储有三种方式,分别为:

Web Storage —— 最简单的 key/value 映射

使用起来就像操作一个普通 object 一样方便,但 key 和 value 都是目前都只支持字符串类型存储。以下我们以 localStorage 为例来说明(sessionStorage的用法基本相同)。

window.localStorage.setItem('a', 'Apple');  document.writeln(window.localStorage.getItem('a'));    window.localStorage['b'] = 'Banana';  document.writeln(window.localStorage['b']);    window.localStorage['c'] = 105;  document.writeln(window.localStorage['c'] + 1);

执行的结果为:

Apple Banana 1051

Web SQL Database —— 如果方向错了,停下来就是进步

SQL 本身并没有一套完备的标准,而即使是这套并不完备的标准的种种规定,也没有一个实现者对其做到了百分之百的支持。SQLite 作为轻量级的数据库管理系统,由于占用系统资源非常的少、支持多操作系统同时还能够跟多种程序语言相结合(如 Tcl、C#、PHP、Java等),目前所有实现了 Web SQL Database 的浏览器都是使用它来做 SQL 后端。但 SQLite 不是标准,也没有完全遵守 SQL 标准,这使得 Web SQL Database 的细则制定进入了一个死胡同。

“如果方向错了,停下来就是进步。”

Web Applications Working Group 显然明白这个道理,于是 W3C 上就有了这样的声明:

Beware. This specification is no longer in active maintenance and the Web Applications Working Group does not intend to maintain it further.

 

Indexed Database —— 更好的数据库API

作为介于 Web Storage 和 Web SQL Database 之间的一种形式,indexedDB具有以下特点:

  • 基于数据库的概念,却不是 SQL Database
  • 与 Web Storage 一样采用 key/value 对的方式但 value 不再只是字符串类型,而是以结构化的 JavaScript 对象的方式存储
  • 既支持异步步的执行方式(通过事件触发来运行),也支持同步的执行方式
  • 需要借助锁的机制来保证数据不会被多个语句函数同时修改

我们来看下面这个例子演示怎样使用 indexedDB :

var books = [{ name: 'Lord of Misrule',  isbn: '0929701836',  author: 'Jaimy Gordon' },               { name: 'Just Kids',        isbn: '0060936223',  author: 'Patti Smith' },               { name: 'Mockingbird',      isbn: '0142417750',  author: 'Kathryn Erskine' },               { name: 'Quaking',          isbn: '014241476X',  author: 'Kathryn Erskine' }              ];    if ('webkitIndexedDB' in window) {    window.indexedDB = window.webkitIndexedDB;    window.IDBTransaction = window.webkitIDBTransaction;  } else if ('moz_indexedDB' in window) {    window.indexedDB = window.moz_indexedDB;  }    function report(info) {    var infoArea = document.getElementById('info');    infoArea.innerHTML += info + '
';  }    function findBooksOfAuthor(author) {    var store = db.transaction([], IDBTransaction.READ_ONLY).objectStore('books');    var index = store.index('BookAuthor');      var req = index.get(author);      req.onsuccess = function(event) {      var matching;      if (event.result) {        matching = event.result;      } else {        matching = req.result;      }      report('Find matching:');      report('> isbn: ' + matching.isbn + ', name: ' + matching.name + ', author: ' + matching.author);    }      req.onerror = function(event) {      report('Cannot find book of this author!');    }  }    function insertBookInformation(value) {    // Create a transaction that locks the world.    var store = db.transaction([], IDBTransaction.READ_WRITE).objectStore('books');    var request = store.add(value, value.isbn);      request.onsuccess = function(event) {      console.log(request.result);      report('Book inserted: ' + (event.result ? event.result : request.result));    }      request.onerror = function(event) {      report(event.message);    }  }    var db;  var dbRequest = window.indexedDB.open('bookstore',  /* name */                                        'Book store'  /* descriptiond */);  dbRequest.onsuccess = function(event) {    report('Openning IndexedDB succeeds!');    if (event.result) {      db = event.result;    } else {      db = dbRequest.result;    }      var versionRequest = db.setVersion('1.0');      versionRequest.ontimeout = function(event) {      report('Timeout on setting version!')    }      versionRequest.onsuccess = function(event) {      report('Setting version succeeds!');          if (db.objectStoreNames.contains('books')) {        if (!db.removeOjbectStore) {          db.deleteObjectStore('books');        } else {          db.removeObjectStore('books');        }      }        var store = db.createObjectStore('books', 'isdn', false);      report('Store created: ' + store);        for (var i = 0; i < books.length; i++) {        insertBookInformation(books[i]);      }        var index = store.createIndex('BookAuthor', 'author', false);      report('Index created: ' + index);      findBooksOfAuthor('Kathryn Erskine');    }  }

执行的结果为:

Openning IndexedDB succeeds!  Setting version succeeds!  Store created: [object IDBObjectStore]  Index created: [object IDBIndex]  Book inserted: 0929701836  Book inserted: 0060936223  Book inserted: 0142417750  Book inserted: 014241476X  Find matching:  > isbn: 0142417750, name: Mockingbird, author: Kathryn Erskine

点击这里查看本段代码在各浏览器中实际运行结果,并可右键查看所有源码。

 

上面这些标准里都有,而标准里没有明确说明却经常被问到的几个问题有:

Q1: 数据存储在哪儿?

Web Storage / Web SQL Database / Indexed Database 的数据都存储在浏览器对应的用户配置文件目录(user profile directory)下,以 Windows 7 为例,Chrome 的数据存储在”C:\Users\your-account-name\AppData\Local\Google\Chrome\User Data\Default\”下,而 Firefox 的数据存储在”C:\Users\your-account-name\AppData\Local\Mozilla\Firefox\Profiles\”目录下。

Q2: 卸载浏览器之后数据还在不在?

如果你在卸载浏览器时主动勾选了同时删除个人数据的选项(如下图所示),那么用户配置文件目录就会被整个删除。当然所有的数据也就不存在了。

如果没有勾选这项的话,下次安装此浏览器后,会发现存储的数据还在。

Q3: 存储的数据是否安全?

我觉得很难去界定这些新的存储技术是“安全的”还是“不安全的”,只能说这些新的存储技术并没有增加更多的安全隐患,他们并没有比传统的 cookies 更安全,但也不会更危险。跨站攻击依然是一个隐患。而 Sandbox 也不会这些数据进行保护。所以我们也希望 Web Application 的开发者们在开发的时候就考虑到这样的问题。

 

推荐阅读:

文章来源:http://enjoyhtml5.com/blogs/storage.html

Leave a Reply