客户端存储技术

客户端数据存储概述

因为浏览器无法记住请求的信息,因此所有的信息必须从服务器端获得。

使用客户端存储的优势是

  • 能够直接访问数据,响应速度更快
  • 能够减少网络访问的流量,减轻服务器的压力

其不足处有

  • 无法提供像数据库那样强大的搜索功能
  • 无法自动更新
  • 存储容量大小模糊无法控制

使用Cookie

cookie是通过HTTP请求头发送到服务器的。在默认情况下,浏览器没有限制Cookie的数量。

Cookie的使用

创建Cookie

创建普通的Cookie

<script>
    var name = "Mickle jiang"
    // 创建一个名为name值为Mickle jiang的Cookie
    // Cookie的值必须符合 URL编码规则,
    //这意味着如果想动态定义 Cookie,那么就需要使用类似 encodeURIComponent的辅助函数
    document.cookie = "username=" + encodeURIComponent(name)
    // 创建一个名为age, 值为20的Cookie
    document.cookie = "age=20"
</script>

创建带有过期时间的Cookie

// Cookie默认的有效期是当前会话
document.cookie = "name=jiangyx; expries=Fri, 31 Dec 9999 23:59:59 GMT"

创建带有指定子域名的Cookie

// Cookie默认是只对当前域名有效
document.cookie = "name=jiangyx; domain=blog.jiangyx.com"

读取Cookie

// 读取Cookie直接使用document.cookie即可获取
console.log(document.cookie)

删除Cookie

// 删除Cookie就是将其有效期设置为过期时间即可
document.cookie = "name=jiangyx; expires=Thu, 01 Jan 1970 00:00:00 GMT";

Cookie操作工具类

var cookieUtil = {
  getItem: function (sKey) {
    if (!sKey) { return null; }
    return decodeURIComponent(document.cookie.replace(new RegExp("(?:(?:^|.*;)\\s*" + encodeURIComponent(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=\\s*([^;]*).*$)|^.*$"), "$1")) || null;
  },
  setItem: function (sKey, sValue, vEnd, sPath, sDomain, bSecure) {
    if (!sKey || /^(?:expires|max\-age|path|domain|secure)$/i.test(sKey)) { return false; }
    var sExpires = "";
    if (vEnd) {
      switch (vEnd.constructor) {
        case Number:
          sExpires = vEnd === Infinity ? "; expires=Fri, 31 Dec 9999 23:59:59 GMT" : "; max-age=" + vEnd;
          break;
        case String:
          sExpires = "; expires=" + vEnd;
          break;
        case Date:
          sExpires = "; expires=" + vEnd.toUTCString();
          break;
      }
    }
    document.cookie = encodeURIComponent(sKey) + "=" + encodeURIComponent(sValue) + sExpires + (sDomain ? "; domain=" + sDomain : "") + (sPath ? "; path=" + sPath : "") + (bSecure ? "; secure" : "");
    return true;
  },
  removeItem: function (sKey, sPath, sDomain) {
    if (!this.hasItem(sKey)) { return false; }
    document.cookie = encodeURIComponent(sKey) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT" + (sDomain ? "; domain=" + sDomain : "") + (sPath ? "; path=" + sPath : "");
    return true;
  },
  hasItem: function (sKey) {
    if (!sKey) { return false; }
    return (new RegExp("(?:^|;\\s*)" + encodeURIComponent(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=")).test(document.cookie);
  },
  keys: function () {
    var aKeys = document.cookie.replace(/((?:^|\s*;)[^\=]+)(?=;|$)|^\s*|\s*(?:\=[^;]*)?(?:\1|$)/g, "").split(/\s*(?:\=[^;]*)?;\s*/);
    for (var nLen = aKeys.length, nIdx = 0; nIdx < nLen; nIdx++) { aKeys[nIdx] = decodeURIComponent(aKeys[nIdx]); }
    return aKeys;
  }
};

Cookie实战

简单地使用 Cookie 统计你访问网站的次数

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <p>当前网站访问人数: <span id="count"></span></p>
    </body>
    <script type="text/javascript" src="js/cookie.js" ></script>
    <script>
        var count = 0
        if (cookieUtil.hasItem('views')) {
            count = parseInt(cookieUtil.getItem('views'))
        }
        count++
        // 更新访问人数
        cookieUtil.setItem('views', count)
        document.getElementById('count').innerText = count
    </script>
</html>

使用Web存储

Web存储

Web存储即本地存储分为两个版本,本地存储(Local Storge)和会话存储(Session Storge)。
本地存储能够持久的存储在浏览器中,会话存储是当浏览器关闭后就会清除。
Web存储是和域名一一对应的。和Cookie不同,在Web存储中,子域名和主域名是隔离的,即www.app.com和blog.app.com中Web存储的数据是隔离不互通的。
Web存储没有容量大小的限制,但是一般为5MB-10MB之间。

使用Web存储

// 设置特定键的值
localStorage.setItem('name', 'mickle')
// 检索特定键的值
console.log(localStorage.getItem('name')) // mickle
// 删除键及与其关联的值
localStorage.removeItem('name')
console.log(localStorage.getItem('name')) // null
// 删除所有的键 / 值对(但只限于发出请求的特定域名)
localStorage.clear()
// 本地存储存储数组的字符串版本
localStorage.setItem('pi', ['222', '333'])
console.log(localStorage.getItem('pi'))
localStorage.setItem('pi2', JSON.stringify(['222', '333']))
// 通过JSON能够维持存储数据的结构
console.log(JSON.parse(localStorage.getItem('pi2')))

使用sessionStorge存储方式和localStorge方式一致

实战代码

使用本地存储来实现访问人数的统计

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <p>当前网站访问人数: <span id="count"></span></p>
    </body>

    <script>
        if (window.localStorage) {
            var count = localStorage.getItem('count')
            if (!count) {
                count = 0
            }
            count++;
            localStorage.setItem('count', count)
            document.getElementById('count').innerText = count
        }
    </script>
</html>

使用IndexedDB

检查IndexedDB的支持

function isIndexDBSupport() {
    return 'indexedDB' in window
}

IndexDB在IOS8上的支持存在问题,因此可以做进一步的限制

function isIndexDBSupport() {
    return 'indexedDB' in window && !/iPad|iPhone|iPod/.test(navigator.platform)
}

连接IndexedDB

<script>
    function isIndexDBSupport() {
        return 'indexedDB' in window && !/iPad|iPhone|iPod/.test(navigator.platform)
    }
    var db = null;
    $(function() {
        // 检查浏览器是否支持 IndexedDB
        if (!isIndexDBSupport()) return;
        // 用 indexedDB.open 方法打开数据库,并指明版本号(从1开始)
        var openRequest = indexedDB.open('db1', 1)
        openRequest.onupgradeneeded = function(e) {
            console.log('数据库更新监听')
        }
        openRequest.onsuccess = function(e) {
            console.log('数据库连接成功')
            // 获取到数据库连接对象
            db = e.target.result
        }
        openRequest.onerror = function(e) {
            console.log('数据库连接异常')
            console.dir(e)
        }
    })
</script>

对象存储

indexedDB中的对象存储类似于SQL中的表

创建对象存储

对象存储的创建应该是在onupgradeneeded中执行。

一般创建对象存储方式为:

  • 更新版本号
  • 在onupgradeneeded中判断对象是否存在
  • 不存在则使用createObjectStore进行创建对象存储
<script>
    function isIndexDBSupport() {
        return 'indexedDB' in window && !/iPad|iPhone|iPod/.test(navigator.platform)
    }
    var db = null;
    $(function() {
        // 检查浏览器是否支持 IndexedDB
        if (!isIndexDBSupport()) return;
        // 用 indexedDB.open 方法打开数据库,并指明版本号(从1开始)
        var openRequest = indexedDB.open('db2', 1)
        openRequest.onupgradeneeded = function(e) {
            console.log('数据库更新监听')
            var thisDB = e.target.result
            if (!thisDB.objectStoreNames.contains('first')) {
                // 如果不存在对象则创建
                console.log('创建存储对象')
                thisDB.createObjectStore('first')
            }
        }
        openRequest.onsuccess = function(e) {
            console.log('数据库连接成功')
            // 获取到数据库连接对象
            db = e.target.result
            // 打印所有的存储对象
            console.log(db.objectStoreNames)
        }
        openRequest.onerror = function(e) {
            console.log('数据库连接异常')
            console.dir(e)
        }
    })
</script>

定义对象存储的主键

和数据库中的表拥有主键一样,对象存储也需要设置一个主键用来进行唯一标识。
对象存储主键有两种设置方式
一、设置key path。它本质上是一个永远存在并且包含唯一信息的属性
二、使用 key generator,它本质上是一种生成唯一值的方式

openRequest.onupgradeneeded = function(e) {
    console.log('数据库更新监听')
    var thisDB = e.target.result
    if (!thisDB.objectStoreNames.contains('people')) {
        // 创建了一个名为 people 的对象存储,并假定每条数据都包含一个名为 email 的唯一属性
        thisDB.createObjectStore('people', {keyPath: 'email'})
    }
    if (!thisDB.objectStoreNames.contains('notes')) {
        // 创建了一个名为 notes 的对象存储,并使用一个自增值自动为主键赋值
        thisDB.createObjectStore('notes', {autoIncrement: true})
    }
    if (!thisDB.objectStoreNames.contains('logs')) {
        // 创建了一个名为 logs 的对象存储。这一次,使用自增值并将其存储在一个名为 id的属性中
        thisDB.createObjectStore('logs', {autoIncrement: true, keyPath: 'id'})
    }
}

如果存储的数据有一个属性应该唯一,那么使用 keyPath选项确保唯一性。如果存储的数据本身没有唯一属性,那么就使用自增值

定义对象存储的索引

在创建对象存储的主键后可以定义索引,索引可以用来让我们根据索引查询数据和以及确定唯一性的约束

openRequest.onupgradeneeded = function(e) {
    console.log('数据库更新监听')
    var thisDB = e.target.result
    if (!thisDB.objectStoreNames.contains('people')) {
        // 创建了一个名为 people 的对象存储,并假定每条数据都包含一个名为 email 的唯一属性
        var peopleOS = thisDB.createObjectStore('people', {keyPath: 'email'})
        // 在性别属性上建立索引,让你可以根据人的性别获取数据
        peopleOS.createIndex('gender', 'gender', {unique: false})
        // 在用户编号属性上建立索引,并且保证其是唯一
        peopleOS.createIndex('ssn', 'ssn', {unique: true})
    }
    if (!thisDB.objectStoreNames.contains('notes')) {
        // 创建了一个名为 notes 的对象存储,并使用一个自增值自动为主键赋值
        var notesOS = thisDB.createObjectStore('notes', {autoIncrement: true})
        notesOS.createIndex('title', 'title', {unique: false})
    }
    if (!thisDB.objectStoreNames.contains('logs')) {
        // 创建了一个名为 logs 的对象存储。这一次,使用自增值并将其存储在一个名为 id的属性中
        thisDB.createObjectStore('logs', {autoIncrement: true, keyPath: 'id'})
    }
}

使用indexedDB操作数据

添加数据

function addPerson() {
    var name = $('#name').val()
    var email = $('#email').val()
    console.log('添加' + name + '/' + email)
    // 获取事物
    var trancation = db.transaction(['people'], 'readwrite')
    // 丛事物中获取存储对象
    var store = trancation.objectStore('people')
    var person = {
        email: email,
        name: name,
        create_time: new Date().getTime()
    }

    var request = store.add(person)
    request.onerror = function(e) {
        console.log("Error",e.target.error.name);
    }
    request.onsuccess = function(e) {
        console.log("成功添加" + name);
    }
}

获取数据

// 根据主键获取用户
function getPerson () {
    var key = $('#getmail').val()
    if (key === '') return
    // 通过数据库连接对象获取一个只读事务
    var transcation = db.transaction(['people'], 'readonly')
    // 获取到people对象存储
    var store = transcation.objectStore('people')
    // 通过主键获取对象信息
    var request = store.get(key)
    request.onsuccess = function(e) {
        var result = e.target.result
        console.log(result)
    }
    request.onerror = function(e) {
        console.log('Error')
        console.log(e)
    }
}

更新数据

// 更新用户
function updatePerson () {
    var email = $('#email2').val()
    var name = $('#name2').val()
    var transcation = db.transaction(['people'], 'readwrite')
    var store = transcation.objectStore('people')
    var person = {
        name: name,
        email: email,
        create_at: new Date().getTime()
    }
    var request = store.put(person)
    request.onsuccess = function (e) {
        console.log('成功更新')
        console.log(JSON.stringify(person))
    }
    request.onerror = function (e) {
        console.log('Error')
        console.log(e)
    }

删除数据

function deletePerson () {
    var key = $('#delete').val()
    if (key === '') return
    var transcation = db.transaction('people', 'readwrite')
    var store = transcation.objectStore('people')
    // 根据主键删除
    var request = store.delete(key)
    request.onsuccess = function(e) {
        console.log("Person deleted");
        console.dir(e);
    }
    request.onerror = function(e) {
        console.log("Error");
        console.dir(e);
    }
}

获取全部

function getAllPeople () {
    var transaction = db.transaction(["people"], "readonly")
    var people = transaction.objectStore("people")
    var cursor = people.openCursor()
    cursor.onsuccess = function (e) {
        var cursor = e.target.result
        if (cursor) {
            console.log(cursor)
            cursor.continue()
        }
    }
    transaction.oncomplete = function() {
        console.log('读取全部完成')
    }
}

完整代码

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <script src="https://cdn.bootcss.com/jquery/3.0.0/jquery.min.js"></script>
    </head>
    <body>
        <h2>Add Person</h2>
        <input type="text" id="name" placeholder="Name"><br/>
        <input type="email" id="email" placeholder="Email"><br/>
        <button id="addPerson">Add Person</button>
        <h2>Add Note</h2>
        <textarea id="note"></textarea>
        <button id="addNote">Add Note</button>
        <hr />
        <p>
            <input type="text" id="getmail" value="" />
            <button id="getPerson">获取用户</button>
        </p>
        <p>
            <input type="text" id="getnote" value="" />
            <button id="getNote">获取备注</button>
        </p>
        <hr />
        <input type="text" id="name2" placeholder="Name"><br/>
        <input type="email" id="email2" placeholder="Email"><br/>
        <button id="updatePerson">更新用户</button>
        <hr />
        <p>
            <input type="text" id="delete" value="" />
            <button id="deletePerson">删除用户</button>
        </p>
        <hr />
        <button id="getAllPeople">获取全部用户</button>
    </body>

    <script>
        function isIndexDBSupport() {
            return 'indexedDB' in window && !/iPad|iPhone|iPod/.test(navigator.platform)
        }
        var db = null;
        $(function() {
            // 检查浏览器是否支持 IndexedDB
            if (!isIndexDBSupport()) return;
            // 用 indexedDB.open 方法打开数据库,并指明版本号(从1开始)
            var openRequest = indexedDB.open('db3', 2)
            openRequest.onupgradeneeded = function(e) {
                console.log('数据库更新监听')
                var thisDB = e.target.result
                if (!thisDB.objectStoreNames.contains('people')) {
                    // 创建了一个名为 people 的对象存储,并假定每条数据都包含一个名为 email 的唯一属性
                    var peopleOS = thisDB.createObjectStore('people', {keyPath: 'email'})
                }
                if (!thisDB.objectStoreNames.contains('notes')) {
                    // 创建了一个名为 notes 的对象存储,并使用一个自增值自动为主键赋值
                    var notesOS = thisDB.createObjectStore('notes', {autoIncrement: true})
                }
            }
            openRequest.onsuccess = function(e) {
                console.log('数据库连接成功')
                // 获取到数据库连接对象
                db = e.target.result
                console.log(db.objectStoreNames)
                $('#addPerson').on('click', addPerson)
                $('#addNote').on('click', addNote)
                $('#getPerson').on('click', getPerson)
                $('#getNote').on('click', getNotes)
                $('#updatePerson').on('click', updatePerson)
                $('#deletePerson').on('click', deletePerson)
                $('#getAllPeople').on('click', getAllPeople)
            }
            openRequest.onerror = function(e) {
                console.log('数据库连接异常')
                console.dir(e)
            }
        })
        // 添加用户
        function addPerson () {
            var name = $('#name').val()
            var email = $('#email').val()
            // 获取事物
            var trancation = db.transaction('people', 'readwrite')
            // 丛事物中获取存储对象
            var store = trancation.objectStore('people')
            var person = {
                email: email,
                name: name,
                create_time: new Date().getTime()
            }

            var request = store.add(person)
            request.onerror = function(e) {
                console.log("Error",e.target.error.name);
            }
            request.onsuccess = function(e) {
                console.log("成功添加" + name);
            }
        }
        // 添加备注
        function addNote () {
            var note = $('#note').val()
            console.log('添加' + note)
            // 获取事物
            var trancation = db.transaction(['notes'], 'readwrite')
            // 从事务请求对象存储
            var store = trancation.objectStore('notes')
            var note = {
                content: note,
                create_time: new Date().getTime()
            }

            var request = store.add(note)
            request.onerror = function(e) {
                console.log("Error",e.target.error.name);
            }
            request.onsuccess = function(e) {
                console.log("成功添加" + note);
            }
        }
        // 根据主键获取用户
        function getPerson () {
            var key = $('#getmail').val()
            if (key === '') return
            var transcation = db.transaction(['people'], 'readonly')
            var store = transcation.objectStore('people')
            var request = store.get(key)
            request.onsuccess = function(e) {
                var result = e.target.result
                console.log(result)
                // 将值设置到更新输入框中
                if (result) {
                    $('#email2').val(result.email)
                    $('#name2').val(result.name)
                } else {
                    $('#email2').val('')
                    $('#name2').val('')
                }

            }
            request.onerror = function(e) {
                console.log('Error')
                console.log(e)
            }
        }
        function getNotes () {
            var key = $('#getnote').val()
            if (key === '') return

            var transcation = db.transaction(['notes'], 'readonly')
            var store = transcation.objectStore('notes')
            var request = store.get(Number(key))

            request.onsuccess = function (e) {
                console.log(e.target.result)
            }
            request.onerror = function (e) {
                console.log('Error')
                console.log(e)
            }
        }
        // 更新用户
        function updatePerson () {
            var email = $('#email2').val()
            var name = $('#name2').val()
            var transcation = db.transaction(['people'], 'readwrite')
            var store = transcation.objectStore('people')
            var person = {
                name: name,
                email: email,
                create_at: new Date().getTime()
            }
            var request = store.put(person)
            request.onsuccess = function (e) {
                console.log('成功更新')
                console.log(JSON.stringify(person))
            }
            request.onerror = function (e) {
                console.log('Error')
                console.log(e)
            }
        }
        function deletePerson () {
            var key = $('#delete').val()
            if (key === '') return
            var transcation = db.transaction('people', 'readwrite')
            var store = transcation.objectStore('people')
            // 根据主键删除
            var request = store.delete(key)
            request.onsuccess = function(e) {
                console.log("Person deleted");
                console.dir(e);
            }
            request.onerror = function(e) {
                console.log("Error");
                console.dir(e);
            }
        }
        function getAllPeople () {
            var transaction = db.transaction(["people"], "readonly")
            var people = transaction.objectStore("people")
            var cursor = people.openCursor()
            cursor.onsuccess = function (e) {
                var cursor = e.target.result
                if (cursor) {
                    console.log(cursor)
                    cursor.continue()
                }
            }
            transaction.oncomplete = function() {
                console.log('读取全部完成')
            }
        }
    </script>
</html>

优秀的第三方库

Locker 简化LocalStorge操作的库
Dexie简化IndexedDB
localForage 封装了多种客户端存储的库,支持IndexedDB、Web SQL 和本地存储
其他各种存储库wiki列表

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值