H5儲存

weixin_34236869發表於2018-06-20

1.本地儲存-Web Storage
2.本地儲存-IndexedDB
3.本地儲存的擴充套件介紹
4.離線儲存-app cache
5.總結

分析儲存需求:

  1. 移動端網路環境因素
  • 資料響應慢,體驗下降,2G/3G網速非常慢
  1. 流量因素
  • 客戶端儲存 = 節省流量 = 節省金錢
  1. 追求原生體驗
  • 離線使用應用
  • 儲存應用相關資源、資料

Cookie 可行否? 否

因為Cookie 的侷限性:

  • 儲存大小限制,僅 4kb 左右
  • 單個域名下的數量限制,50個左右
  • 汙染請求頭,浪費流量

說明:

  • cookie的作用是與伺服器進行互動,作為HTTP規範的一部分而存在 ,
    而Web Storage僅僅是為了在本地“儲存”資料而生。
  • 每次你請求一個新的頁面的時候Cookie都會被髮送過去,這樣無形中浪費了頻寬。

本地儲存:localStorage 和 sessionStorage

  1. 相同的使用方法
  • 使用 setItem(key,value) 方法設定儲存內容
  • 使用 getItem(key) 方法獲取儲存內容
  • 使用 removeItem(key) 方法刪除儲存內容
  • 使用 clear() 方法清除所有內容
  • 使用 length 屬性獲取儲存內容個數
  • 使用 key(index) 方法獲取儲存欄位
  1. 不同的儲存時效
  • localStorage 儲存會持久化
  • sessionStorage 儲存會在網頁會話結束(標籤頁的關閉)後失效,只有在同一個會話中的頁面才能訪問。
  1. 不同的儲存容量
  • localStorage 容量一般在 2 – 5Mb 左右
  • sessionStorage 儲存容量不一,部分瀏覽器不設限
  1. Web Storage Support Test
    http://dev-test.nemikor.com/web-storage/support-test/

sessionStorage和localStorage的儲存空間雖然較大,但是儲存容量仍有限制,叫做配額。

  1. 使用 Storage 時的注意點:
  1. 儲存容量超出限制
  • 丟擲 QuotaExceededError 異常 (儲存值時應使用 try catch 捕獲異常)
<!doctype html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <title>quota exceed test</title>
</head>

<body>
    <button class="start-btn">start</button>
    <script>
    var btn = document.querySelector('.start-btn');


    var data = '01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890',
        id = 0,
        len = data.length,
        total = 0,
        key;

    function save() {
        id = (id + 1) % 10;
        key = Date.now() + '' + id;
        // try...catch 主要用於捕獲程式碼執行時的異常,並進行異常處理。
        // try 部分包含執行時,可能出現異常的程式碼.
        // 而 catch 部分包含錯誤發生時執行的程式碼。
        try {
            // try 中寫可能產生異常的語句
            localStorage.setItem(key, data);
            save();
        } catch (e) {
            // catch 中寫負責異常處理的語句
            console.log(e.name); //QuotaExceededError

        }

    }
    btn.addEventListener('click', save);

    </script>
</body>

</html>
  1. 儲存型別的限制
  • 僅能儲存字串
  • 注意型別轉換 JSON.stringifyJSON.parse
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>quota exceed test</title>
</head>
<body>
    <button class="str" onclick="save('abc')">字串 abc</button><br />   
    <button class="bool" onclick="save(false)">布林值 false</button><br />
    <button class="undef" onclick="save(window.undefined)">未定義 window.undefined</button><br />
    <button class="null" onclick="save(null)">空值 null</button><br />
    <button class="num" onclick="save(0)">數字 0</button><br />
    <button class="arr1" onclick="save([])">陣列 []</button><br />
    <button class="arr2" onclick="save([1,2,3])">陣列 [1,2,3]</button><br />
    <button class="obj1" onclick="save({})">物件 {}</button><br />
    <button class="obj2" onclick="save({x: 1})">物件 {x: 1}</button><br />
    <button class="obj3" onclick="save({toString: function () {return 'toStringed'}})">物件 {toString: function () {return 'toStringed'}}</button><br />
    <button class="obj4" onclick="save({toString: function () {return 100}})">物件 {toString: function () {return 100}}</button><br />
    
    
<script>
function save (data) {
    localStorage.setItem('key', data)
    show(localStorage.getItem('key'))
}

function show (value) {
    console.log(value, '|', typeof value)
}

</script>
</body>
</html>  

列印結果:

> abc | string
> false | string
> undefined | string
> null | string
> 0 | string
>  | string
> 1,2,3 | string
> [object Object] | string
> [object Object] | string
> toStringed | string
> 100 | string

型別轉換:在控制檯輸入

> localStorage.setItem('key',JSON.stringify({data:1}))
< undefined
> localStorage.getItem('key')
< "{"data":1}"
> JSON.parse(localStorage.getItem('key'))
< {data: 1}
  1. sessionStorage 失效機制
  • 重新整理頁面並不會失效 location.reload()
  • 相同 URL 不同標籤頁不能共享 sessionStorage
  1. Web Storage 的優化
    效能與儲存容量大小無關,與讀取次數有關
  • 減少讀取 item 次數
  • 單個 item 中儘可能多的儲存資料
  1. 帶有過期機制的 localStorage 的功能需求
  1. 可以設定資料的儲存時間
  2. 過期資料清理
  3. 自行維護儲存空間
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>cache storage</title>
</head>
<body>  
<script>
'use strict'
;(function () {
    var ls = window.localStorage

    function oops () {
        return console.warn('your browser is not supported localStorage API')
    }

    function getItem (key) {
        var data = ls.getItem(key)  //在沒有資料項的情況下,data為null
        data = JSON.parse(data)||{} //將字串型別的data轉為物件型別的data

        if (data.time === 0) {// 上句如是data = JSON.parse(data),在data.time時,就會報錯。所以調整成data = JSON.parse(data)||{}
            // 持久化資料直接返回
            return data.value
        } else if (Date.now() > data.time) { // 判斷是否超時
            //過期
            ls.removeItem(key) 
            return ''
        } else {
            // 沒過期
            return typeof data.value !== 'undefined' ? data.value : ''
        }
    }

    function setItem (key, value, time) {
        if (typeof key === 'undefined') {return}
        var data = {
            time: time ? Date.now() + time : 0,
            value: value
        }
        data = JSON.stringify(data) //stringify方法進行物件的字串序列化
        try {
            ls.setItem(key, data)
        } catch (e) {
            ls.clear()
            ls.setItem(key, data)
        }
        
    }

    function removeItem (key) {
        ls.removeItem(key)
    }

    function clear () {
        ls.clear()
    }

    window.cacheStorage = {//瀏覽器支援使用localStorage,不支援輸出提示語
        getItem: ls ? getItem : oops,
        setItem: ls ? setItem : oops,
        removeItem: ls ? removeItem : oops,
        clear: ls ? clear : clear
    }
})()
</script>
</body>
</html>

IndexedDB 資料庫

1.瞭解indexedDB資料庫

1)indexedDB資料庫是一種事務型資料庫
2)是NoSQL資料庫
3)使用JS物件儲存資料

2.如何建立資料庫和表

  1. 如何建立資料庫和"表"?
  1. indexedDB.open('dbName',dbVersinNumber) 建立資料庫,返回 IDBOpenDBRequest 物件
  2. indexedDB.createObjectStore 建立“表”
  3. indexedDB.deleteDatabase('dbName') 刪除資料庫
    function createDB() {
        // request 是 IDBOpenDBRequest物件。 
        request = db.open(dbName, version)
        //請求有三種狀態,如下:
        request.onsuccess = function() { // 開啟資料庫成功
            db = request.result;
            console.log('open success');

        }

        request.onerror = function(e) { // 開啟資料庫失敗
            console.log(e.currentTarget.errormessage)
        }

        request.onupgradeneeded = function(e) { //請求資料庫版本變化時
            var store = null;
            db = e.target.result;
            console.log('upgradeneeded');

            /*
            if (!db.objectStoreNames.contains(osName)) {
                db.createObjectStore(osName, {autoIncrement: true}) // 建立的表的主建是自增型的
            }
            */

            if (!db.objectStoreNames.contains(osName)) {
                store = db.createObjectStore(osName, { keyPath: 'id' }) // 建立的表以欄位為主建
                store.createIndex('idIndex', 'id', { unique: true }); //建立索引欄位id唯一
                store.createIndex('categoryIndex', 'category', { multiEntry: true }); //建立索引欄位為陣列
                store.createIndex('hpIndex', 'hp', { unique: false });
            }                
           
        }

        // onsuccess事件在onupgradeneeded事件之後觸發
    }
  1. 設定主鍵的兩種方法
  1. 設定自增主鍵 - {autoIncrement: true}
db.createObjectStore(osName, {autoIncrement: true})
1659442-50f936785529f965.png
  1. 取資料中欄位作為主鍵 - {keyPath: 欄位名}
store = db.createObjectStore(osName, { keyPath: 'id' })
1659442-9f0137b6a003f2a6.png

3. 關於表的增刪改查

  1. 如何使用事務獲取表
    呼叫 IDBDatabase.transaction 方法會返回一個 IDBTransaction 物件,
    它含有一個 objectStore 方法, 可以讓使用者通過指定模式運算元據庫中的“表”。

indexedDB -> transaction -> objectStore

//osName 表格名稱,
var db = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
var transaction = db.transaction(osName, 'readwrite'), //開啟一個事務,讀寫模式
store = transaction.objectStore(osName);
  1. 事務的模式
  1. 讀寫模式 - readwrite
  2. 只讀模式(預設) - readonly
  3. 版本變更模式 - versionchange
  1. 關於“表”的增刪查改的相關方法
  1. 增加資料 – IDBObjectStore.add
  2. 獲取資料 – IDBObjectStore.get
  3. 獲取所有資料 – IDBObjectStore.getAll
  4. 修改(或新增)資料 – IDBObjectStore.put
  5. 刪除資料 – IDBObjectStore.delete
  6. 清除所有資料 – IDBObjectStore.clear
    這些方法都返回一個 IDBRequest 物件。
    function addData() {
        if (!db) { alert("db"); }

        // 呼叫 `IDBDatabase.transaction` 方法會返回一個 IDBTransaction 物件,
        // 它含有一個 objectStore 方法, 可以讓使用者通過指定模式運算元據庫中的“表”。
        var transaction, store;
        transaction = db.transaction(osName, 'readwrite'); //開啟一個事務,讀寫模式
        store = transaction.objectStore(osName) ///獲取osName指定的 object store

        data.map(function(o) {
            store.add(o)
            // request = store.add(o) //增加資料
            //   if (data.length - 1 === i) {
            //     request.onsuccess = function () {
            //       console.log('alreay add all data to db')
            //       showCurrentData()
            //     }
            //   }
        })
    }

    function getData(id) {
        var transaction = db.transaction(osName, 'readwrite'), //開啟一個事務,讀寫模式
            store = transaction.objectStore(osName);
        var request = store.get(id);
        request.onsuccess = function() {
            console.log(request.result);
        }

    }


    function getAllData() {
        var transaction = db.transaction(osName, 'readwrite'), //開啟一個事務,讀寫模式
            store = transaction.objectStore(osName);
        var request = store.getAll();
        request.onsuccess = function() {
            console.log(request.result);
        }

    }


    function updateData(id) {
        var transaction = db.transaction(osName, 'readwrite'), //開啟一個事務,讀寫模式
            store = transaction.objectStore(osName);
        var request = store.get(id);
        request.onsuccess = function() {
            request = store.put({ //更新即可以更新也可以新增,取決於關鍵字是否有重複
                name: '小花貓',
                id: id,
                hp: 9
            });
        }

    }


    function deleteData(id) {
        var transaction = db.transaction(osName, 'readwrite'), //開啟一個事務,讀寫模式
            store = transaction.objectStore(osName);
        var request = store.delete(id);

        request.onsuccess = function() {
            console.log('delete success');
        }

    }

    function clear() {
        var transaction = db.transaction(osName, 'readwrite'), //開啟一個事務,讀寫模式
            store = transaction.objectStore(osName);
        var request = store.clear();
        request.onsuccess = function() {
            console.log('clear success');
        }

    }
  1. IDBRequest 物件
  1. 使用 IDBRequest.onsuccess 執行查詢完成回撥
  2. 使用 IDBRequest.result 獲取查詢結果
  3. 使用 IDBRequest.onerror 執行查詢失敗回撥

4. 關於索引

  1. 如何建立索引 IDBObjectStore.createIndex
  • indexName: 索引名稱
  • keyPath: 索引欄位,可以為空或者陣列(type array)
  • optionParameters: 索引配置引數
store = db.createObjectStore(osName, { keyPath: 'id' }) // 建立的表以欄位為主建
store.createIndex('idIndex', 'id', { unique: true }); //建立索引欄位id唯一
store.createIndex('categoryIndex', 'category', { multiEntry: true }); //建立索引欄位為陣列
store.createIndex('hpIndex', 'hp', { unique: false });
1659442-8b9d72902629ffe6.png
1659442-fc9a4b0fc67deb4d.png
1659442-40bb2cc1d1ba97bf.png
  1. optionParameters: 索引配置引數
  1. unique 表示keyPath欄位的資料是否唯一
    2)multiEntry 表示是否為 keyPath 欄位的每一項建立一條索引資料
  1. 使用索引的好處
  1. 可以使用儲存記錄中的值進行檢索
  2. 索引自動更新
  3. 索引資料自動排序
  1. 索引的相關方法
    1)查詢資料 - IDBIndex.get
  1. 查詢所有資料 - IDBIndex.getAll
  2. 開啟遊標 - IDBIndex.openCursor
function useIndexGetData() { // 索引只能查詢資料,並不能運算元據
    var transaction = db.transaction(osName, 'readwrite'), //開啟一個事務,讀寫模式
        store = transaction.objectStore(osName),
        index = store.index('categoryIndex');
        request = index.getAll('飛行');

    request.onsuccess = function() {
        console.log(request.result);
    }
}

5. 關於遊標

  1. 如何建立遊標?
    IDBObjectStore/IDBIndex.openCursor
  • 接收可選引數 range 和 direction,指明遊標遍歷的範圍和方向
  • 返回一個 IDBRequet 物件,非同步方法
  • 該 IDBRequet 物件的結果是一個 IDBCursor 物件
  1. IDBKeyRange 物件
    1)upperBound: 指定遊標範圍的上限
    2)lowerBound: 指定遊標範圍的下限
    3)bound: 指定遊標範圍的區間
    4)only: 指定遊標的值

key range 取值表:


1659442-6fcc5f5725115d91.png

1659442-8d15fc366b57354f.png
  1. direction 引數
  • next: 順序查詢
  • nextunique: 順序唯一查詢
  • prev: 逆序查詢
  • prevunique: 逆序唯一查詢
function useCursorGetData() {
    var transaction = db.transaction(osName, 'readwrite'), //開啟一個事務,讀寫模式
        store = transaction.objectStore(osName),
        // request=store.openCursor();  //建立遊標
        // request=store.openCursor(IDBKeyRange.only('002'));  //指定遊標KeyRange,only一個
        // request = store.openCursor(null, 'prev');
        request = store.openCursor(IDBKeyRange.lowerBound('002'), 'prev'); //逆序查詢,遊標範圍下限是"002"
    request.onsuccess = function() {
        var cursor = request.result;
        if (cursor) {
            console.log(cursor.value);
            cursor.continue();
        }
    }
}

6. 索引和遊標的結合使用

  1. 索引和遊標的優勢
    索引:可以按值搜尋
    遊標:可以選擇遍歷順序及運算元據
function useIndexAndCursorOperateData1() { // 索引和遊標結合,可以查詢和運算元據
    var transaction = db.transaction(osName, 'readwrite'), //開啟一個事務,讀寫模式
        store = transaction.objectStore(osName),
        index=store.index('categoryIndex');
        request=index.openCursor();

    request.onsuccess = function() {
        var cursor = request.result;
        if (cursor) {
            if(cursor.value.id==='002'){

               /* // 更新資料
               cursor.update({  
                      name: '小蝙蝠',
                      id: '002',
                      hp: 10,
                      category:['怪物','飛行']
                })
                */ 

                 // 刪除資料
                cursor.delete().onsuccess=function () {  
                    console.log('delete success');         
                }
                        
            }
            // 更新資料和刪除資料操作都是非同步操作,所以輸出可能會混亂
            console.log(cursor.value);
            cursor.continue();
        }
    }
}

function useIndexAndCursorOperateData() { // 索引和遊標結合,可以查詢和運算元據
    var transaction = db.transaction(osName, 'readwrite'), //開啟一個事務,讀寫模式
        store = transaction.objectStore(osName),
        index = store.index('hpIndex');
        // request = index.openCursor(IDBKeyRange.upperBound(5)); // 5(包含5)以下
        request = index.openCursor(IDBKeyRange.bound(5,10,true,true)); // 範圍(5到10)

    request.onsuccess = function() {
        var cursor = request.result,value=null;
        if (cursor) {
            
            value = cursor.value;
            value.hp += 20;
            cursor.update(value);
            
            console.log(cursor.value);
            cursor.continue();
        }
    }
}

IndexedDB 與 Web Storage 比較

  1. indexedDB 的優勢:
  • 儲存型別更加豐富
  • 可實現高階查詢
  • 可在 Web Workers 中使用
  • 儲存容量更大
  1. Web Storage 的優勢
  • API 較少,更容易掌握
  • 相容性更好
  1. IndexedDB 的相容性問題
    1)IOS8 & 9 中 webview 不支援 indexedDB
    2)Firefox 單次儲存 Blob 資料超 50Mb 會丟擲異常, 這個50M可以在IndexDB的一些API中進行修改
    3)Safari 的 indexedDB 不能用於 web workers
    4)Chrome36 不支援儲存型別的資料

其他儲存方式介紹

1. WebSQL

  • 關係型資料庫
  • 隨HTML5規範加入, 在瀏覽器端執行的輕量級資料庫。
  1. WebSQL 的 API 有哪些?
  • openDatabase: 開啟資料庫
  • transaction: 獲取事務,進行資料庫操作
  • executeSql: 執行 SQL 進行查詢
  1. WebSQL 的現狀
  • 相容性問題嚴重


    1659442-d7bb641bb751e977.png
  • W3C 已經不再積極處理其相關規範(避免使用WebSQL)

2. Filesystem & FileWriter API

  • 供我們在客戶端進行檔案的儲存
  • 但是和WebSQL有相似的命運,有嚴重的相容性問題和規範被廢棄(避免使用)。


    1659442-e4c8f9be76107d36.png

3. UserData

是IE獨有的儲存方式:

  • 只在Windows系統的IE中存在
  • 容量不大

4. Cookie

  • 儲存大小限制,僅 4kb 左右
  • 單個域名下的數量限制,50個左右
  • 汙染請求頭,浪費流量

相關文章