Web 存儲技術

1、背景介紹

第一個Web存儲的技術叫作Cookie,它是網站的身份證。是網站爲了辨別用戶身份,進行session(服務端的session)跟蹤而存儲在用戶本地終端上的數據,也就是說它是存在電腦硬盤上的,一個很小的txt類型的文件。Cookie每次都會跟隨http請求發送到服務端,也就是說每個http請求都會帶上咱們的cookie數據,所以它存在一個安全性的問題。html

cookie自己也是有很大的侷限性的,首先它很小,主流的瀏覽器最大支持 4096 字節,除了最大字節的限制,每一個網站的cookie個數(也就是每個first每個域)也是有限制的,通常瀏覽器是20個。除此以外,cookie還會默認跟隨全部http請求發送,即便不須要使用這個cookie來鑑別用戶可是它也是會跟隨http請求發送的,這樣就會形成一個網絡資源的浪費。而後部分的瀏覽器還限制了總的cookie個數300個。web

在cookie的諸多侷限性下,Web Storage應運而生。Web Storage 解決了不少問題:數據庫

好比它支持存儲大量數據,支持複雜的本地數據庫,並且也不會默認跟隨http請求。Web Storage主要是有四個:數組

  • SessionStorage
  • LocalStorage
  • WebSQL
  • indexedDB

2、Cookie的簡單介紹

Cookie是HTML4的一個標準,它通常不須要考慮兼容。它是網站的一個身份證,服務器能夠針對不一樣用戶,作出不一樣的響應。cookie存儲在用戶的機器上是一個純文本,就是一個txt文件並非一個腳本,它不能執行東西只負責記錄。瀏覽器每次請求都會帶上當前網站的cookie。瀏覽器

Cookie分爲兩種類型,一種呢是會話cookie,也就是臨時性的cookie,退出瀏覽器或者是關閉即刪除;安全

另外一種叫持久cookie,它會一直存在,存在的時間由特定的過時時間或者是有效期來決定。服務器

Cookie的域 Domain決定了當前的一個cookie的權限,哪個域可使用這個cookie。cookie

Cookie的路徑 Path,下面一個簡單的例子:網絡

www.baidu.com                id="123456" domain="www.baidu.com"
www.baidu.com/user           id="123456" user="eric" domain="www.baidu.com" path="/user/"

www.baidu.com/search         id="123456";
www.baidu.com/user/search    id="123456" user="eric";

如上www.baidu.com設置了一個id等於123456,domain是 www.baidu.com,而後另一個跟第一個同樣多設置了一個user,id相同,可是多了一個user=「Eric」,它的domain設置成了 www.baidu.com,path就到了user下面。這二者設置完成以後,當咱們訪問 www.baidu.com/search 時百度只能拿到id,由於user="Eric"是屬於user這個域下面的,也就是說在search下面是獲取不到的,可是在 www.baidu.com/user/search 這個時候咱們就能夠獲取到名叫Eric的user。Path也是一種權限的控制只是相較於域domain是低一級的。session

Cookie的安全secure,若是這個屬性爲TRUE,那麼網站只有在https的請求下面纔會攜帶當前的cookie。

Cookie的HttpOnly這個屬性若是爲TRUE,那麼就不容許JavaScript操做cookie。

由於cookie是存儲在客戶端一個獨立的文件,所以服務器是沒法分辨用戶和攻擊者的。關於cookie的目的分爲兩種:一種是跨站點腳本攻擊,一種是跨站請求僞造。

3、SessionStorage

key-value的鍵值對,是HTML5新增的一個會話存儲對象。

SessionStorage是臨時保存在同一窗口,也就是同一標籤頁的數據。若是當前標籤頁關閉了,那麼SessionStorage也就失效了。這也是SessionStorage最顯著的一個特色:單頁標籤限制。

除此以外,它還有的一些特色有:

  • 同源策略,也就是在同一協議,同一主機名和同一端口下的同一tab
  • 只在本地存儲,不會跟隨http請求發送到服務器
  • 存儲方式採用key-value鍵值對,這裏面的value只能存字符串類型,若是存其餘的會自動轉換成字符串。
  • 存儲上線限制達到了5MB,若是當前存儲超出上限新的內容會把舊的內容覆蓋但不會報錯。

屬性

  • sessionStorage.length - 鍵值對數量
  • sessionStorage.key(int index) -> null
  • sessionStorage.getItem(string key) -> null
  • sessionStorage[string key]
  • sessionStorage.setItem(string key, string value)
  • sessionStorage.removeItem(string key)
  • sessionStorage.clear()

Json對象

  • JSON.stringify()
  • JSON.parse()

4、LocalStorage

LocalStorage也是在瀏覽器的Application下面有一個Local Storage,它和SessionStorage是十分類似的,一樣是key-value鍵值對,也是HTML5的新增存儲對象,它與SessionStorage的特色不一樣之處在於沒有標籤頁的限制和在瀏覽器的無痕模式下LocalStorage是不容許讀取的,永久性的存儲,而後SessionStorage超出限制是覆蓋不會報錯而LocalStorage超出會報錯。

特色

  • 同源策略,也就是在同一協議,同一主機名和同一端口下的同一tab
  • 沒有標籤頁的限制
  • 只在本地存儲,不會跟隨http請求發送到服務器
  • 存儲方式採用key-value鍵值對,這裏面的value只能存字符串類型,若是存其餘的會自動轉換成字符串。
  • 存儲上線限制達到了5MB,若是當前存儲超出上限會報錯。
  • 無痕模式下不可讀取
  • 永久性存儲

屬性

  • sessionStorage.length - 鍵值對數量
  • sessionStorage.key(int index) -> null
  • sessionStorage.getItem(string key) -> null
  • sessionStorage[string key]
  • sessionStorage.setItem(string key, string value)
  • sessionStorage.removeItem(string key)
  • sessionStorage.clear()

注意事項:LocalStorage和SessionStorage在web view是不可靠的,web view指的是在開發混合APP的時候使用了瀏覽器來實現咱們的APP,這個時候是不可靠的,由於在瀏覽器崩潰的狀況下數據可能沒有存進去。

另一個在IOS瀏覽器中不可重複setItem,若是重複會報錯,而後這個時候咱們須要先removeItem再添加item。

監聽storage的變化

監聽storage包括SessionStorage和LocalStorage。而後這裏須要提到兩個概念:同源和監聽同源網頁。

  • 同源:協議、域名、端口三者相同,同源的狀況下咱們能夠共享SessionStorage和LocalStorage。 同源策略還禁止不一樣源執行任何腳本。
    http://localhost:63342/simpleApp/app/index.html#/
        (協議)   (域名)   (端口)         (路徑)
  • 監聽同源網頁,可是同一網頁是無效的
    window.addEventListener("storage", function (event) {
            console.log(event.key);
            console.log(event.oldValue);
            console.log(event.newValue);
            console.log(event.url);
            console.log(event.storageArea);
        });

5、IndexedDB

IndexedDB 背景

  • Storage(Storage指的是SessionStorage和LocalStorage)不適合存儲大量的數據
  • Storage不能提供搜索功能
  • Storage不能創建索引,存儲的內容也比較少
  • IndexedDB擴大了web存儲的容量,能夠達到250MB以上

基本概念

首先它是一個NoSQL,也就是一個非關係型數據庫。MySQL和Oracle都是關係型數據庫。意思就是說若是創建了兩個表在關係型數據庫裏面咱們能夠經過一個外鏈把多個表聯繫起來,可是NoSQL不行,在NoSQL裏面若是想要多個表關聯起來,咱們只能手動的在關聯的表裏面添加上須要關聯的另一個或多個表的id。這是NoSQL與MySQL二者之間的一個區別。

IndexedDB的特色也是和Storage是同樣的:

  • 鍵值對儲存 ,可是容許全部類型,不容許主鍵重複報錯
  • 是一個異步操做, 不阻塞瀏覽器線程
  • 支持事務,事務是SQL數據庫的一個概念,也就是說咱們進行任何的增刪改查都要在某一個事務下面進行,提供了一個回滾功能,一系列操做有一步失敗, 數據庫回滾到事務發生以前的狀態,這樣爲了不操做中途出現失敗,影響整個數據庫的狀態
  • 同源限制
  • 支持二進制儲存

IndexedDB幾個基本概念:

  • IDBDatabase - 數據庫
  • IDBObjectStore - 對象倉庫
  • IDBIndex - 索引
  • IDBTransaction - 事務
  • IDBRequest - 操做請求
  • IDBCursor - 指針
  • IDBKeyRange - 主鍵集合

IndexedDB瀏覽器兼容

IDBDatabase

IDB是IndexedDB的縮寫,它呢就是數據庫,數據庫也叫做數據的一個容器。每個源(同源策略)能夠創建不少數據庫。Database有一個版本的概念,版本對應着數據庫表,同一時刻只能存在一個版本。好比:新增一個表,而後咱們須要把database的版本加一,表裏面要新增一列,這時一樣須要把數據庫版本加一。

注意:1 同一時刻只能有一個版本存在 2 修改數據庫結構只能經過升級數據庫版本

  • 打開數據庫
    /* databaseName不存在則建立 */
     /* version爲整數, 新建時爲1 */
    
     let database;
     let userStore;
     const request = window.indexedDB.open(databaseName, version);
    
     /* 成功打開數據庫 */
     request.onsuccess = event => {
           database = request.result;
     }
    
     /* 打開數據庫失敗 */
     request.onerror = error => {
           console.log(error);
     }
    
     /* 版本號大於當前數據庫版本 */
     request.onupgradeneeded = event => {
           database = event.target.result;
     }

注意:若是在打開數據庫時,數據庫不存在,將會新建一個

IDBObjectStore(數據庫表)

建立表,最好是在upgradeneeded下執行;在建立數據庫表的時候須要指定主鍵,主鍵表明了惟一的標識,好比 keyPath:‘id’;若是不指定主鍵,咱們能夠指定一個autoIncrement:true,自增的一個概念,也就是不指定主鍵數據庫會自動添加主鍵並且這個主鍵就是數字,依次遞增的。

const createStore = () => {
 	//若是當前的objectStoreNames.contains包含user,若是不包含user這個表,而後就用這個database.createObjectStore建立了一個表,這個表的名字就叫作user,而後主鍵就是下面的id
       if(!db.objectStoreNames.contains('user')) {
           userStore = database.createObjectStore('user', { keyPath: 'id' });
       }
 }

指定索引:

const createStore = () => {
       if(!database.objectStoreNames.contains('user')) {
         userStore = database.createObjectStore('user', { keyPath: 'id' });
         userStore.createIndex('name', 'name', { unique: true });
     }
 }

IDBTransaction(事務)

建立完以後須要往裏面添加數據,添加數據咱們就須要使用到事務。

事務涉及到數據庫的增刪改查,它有三個狀態:

  • complete
  • error
  • abort

屬性:

  • IDBTransaction.db 當前數據庫
  • IDBTransaction.mode 模式,使用模式分爲readonly和readwrite
  • IDBTransaction.objectStoreNames 當前數據庫涉及到的哪幾個數組表
  • IDBTransaction.error 回調

數據庫的基本操做:增刪改查以及清空。

新增數據(add)

分爲兩種狀況:一種是使用自增的數據庫的id或者是自增的一個鍵值,若是已經建立主鍵,那麼新增必須包含主鍵和另外一種已建立主鍵但主鍵不可重複。

const add = () => {
     /* 建立事務 */
     /* 使用某個數據庫 */
     /* add新增 */
     transactionRequest = database.transaction(['user'], 'readwrite')
         .objectStore('user')
         .add({ id: 100, name: 'Eric', age: 28, email: 'Ericlee00@163.com' });
 
     /* 成功 */
     transactionRequest.onsuccess = event => {
         console.log('數據寫入成功', event);
     };
 
     /* 失敗 */
     transactionRequest.onerror = error => {
         console.log('數據寫入失敗', error);
     }
 }

讀取數據(get)

const read = () => {
       /* 建立事務 */
       transaction = database.transaction(['user']);
       /* 選擇數據庫表 */
       table = transaction.objectStore('user');
       /* 讀取數據 */
     transactionRequest = table.get(2);
 
     /* 成功 */
     transactionRequest.onerror = event => {
           console.log('數據讀取失敗', event);
 	};
 
     /* 失敗 */
       transactionRequest.onsuccess = event => {
           if (transactionRequest.result) {
               console.log('數據讀取成功', transactionRequest.result);
           } else {
               console.log('未讀取到數據');
           }
       };
 }

更新數據(put)

更新不存在的數據時會新建,也就是說在新增數據時若是相同,每每會出錯,可是在更新數據時不會出錯。若是數據不存在就會新建,若是存在就會一直更新。

const update = () => {
       transactionRequest = database.transaction(['user'], 'readwrite')
           .objectStore('user')
           .put({ id: count, name: 'David', age: 35, email: 'David@xiakedao.com' });
 
       transactionRequest.onsuccess = function (event) {
           console.log('更新數據成功', event);
       };
 
       transactionRequest.onerror = error => {
           console.log('更新數據失敗', error);
       }
   }

刪除數據(delete)

const delete = () => {
       transactionRequest = database.transaction(['user'], 'readwrite')
           .objectStore('user')
           .delete(2);
 
       transactionRequest.onsuccess = function (event) {
           console.log('刪除數據成功', event);
       };
 
       transactionRequest.onerror = error => {
           console.log('刪除數據失敗', error);
       }
   }

清空數據(clear)

IDBCursor(指針)

提供了一種遍歷數據的可能。

const readAll = () => {
       table = database.transaction('user').objectStore('user');
 
       table.openCursor().onsuccess = () => {
           let cursor = event.target.result;
 
           if (cursor) {
               console.log('數據遍歷', cursor);
           cursor.continue();
           } else {
               console.log('數據遍歷完成');
           }
       };
   }

關閉IndexedDB數據庫鏈接

const closeDataBase = () => {
      database.close();
  }

刪除IndexedDB數據庫前,須先關閉數據庫鏈接

const deleteDataBase = () => {
      indexedDB.deleteDatabase('first_database');
  }

6、WebSQL

基本概念:並非 HTML5 的規範 , 只能算是一個獨立的規範;使用WebSQL是完徹底全的SQL 語句,使用SQL語句來操做客戶端數據庫;它一共有三個比較重要的概念,分別是:openDatabase 打開數據庫,能夠是使用現有數據庫或者新建數據庫;transaction 事務,全部的數據庫都支持事務;executeSql 執行SQL語句。

openDatabase(打開數據庫)

相比於IndexedDB的概念稍微多一點,主要是有數據庫名稱、版本號(在IndexedDB裏面版本號都是整數,可是在WebSQL裏面它能夠是小數)、描述文本(介紹數據庫是幹什麼的)、數據庫大小和建立回調(function,只在第一次建立的時候纔會調用)。

const database = openDatabase('my_database', '1.0', 'first', 2 * 1024 * 1024, function() {
 
 });

Transaction(事務)

  • 建立表
    const createTable = () => {
              database.transaction(function (content) {  
                  content.executeSql('CREATE TABLE IF NOT EXISTS USER (id unique, name)');
              });
        }
  • 添加數據
    const addData = () => {
              database.transaction(function (content) {  
                  content.executeSql('INSERT INTO USER (id, name) VALUES (1, "Eric")');
              });
        }
  • 查詢數據
    const searchData = () => {
              database.transaction(function (content) {  
                  content.executeSql('SELECT * FROM USER');
              });
        }
  • 更新數據
    const updateData = () => {
              database.transaction(function (content) {  
                  content.executeSql('UPDATE USER SET name=\'David\' WHERE id=1');
              });
        }
  • 刪除數據庫表
    const deleteDataBase = () => {
              database.transaction(function (content) {  
                  content.executeSql('DROP TABLE USER');
              });
        }
相關文章
相關標籤/搜索