H5存储

作者: lrxxhyf | 来源:发表于2018-06-20 00:12 被阅读54次

    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})
    
    1. 取数据中字段作为主键 - {keyPath: 字段名}
    store = db.createObjectStore(osName, { keyPath: 'id' })
    

    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 });
    
    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 取值表:



    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 的现状
    • 兼容性问题严重


    • W3C 已经不再积极处理其相关规范(避免使用WebSQL)

    2. Filesystem & FileWriter API

    • 供我们在客户端进行文件的存储
    • 但是和WebSQL有相似的命运,有严重的兼容性问题和规范被废弃(避免使用)。


    3. UserData

    是IE独有的存储方式:

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

    4. Cookie

    • 存储大小限制,仅 4kb 左右
    • 单个域名下的数量限制,50个左右
    • 污染请求头,浪费流量

    相关文章

      网友评论

          本文标题:H5存储

          本文链接:https://www.haomeiwen.com/subject/rgazeftx.html