web Storage相关概念

熟悉的web存储主要有以下几种:

- Cookie

- Web storage API

- IndexedDB

- Web Sql

Cookie:

概述

  1. 服务器保存在浏览器的一小段文本信息,一般大小不能超过4KB。浏览器每次向服务器发出请求,就会自动附上这段信息。
  2. HTTP 协议不带有状态,有些请求需要区分状态,就通过 Cookie 附带字符串,让服务器返回不一样的回应。
  3. Cookie 不是一种理想的客户端存储机制。它的容量很小(4KB),缺乏数据操作接口,而且会影响性能。客户端存储建议使用 Web storage API 和 IndexedDB。只有那些每次请求都需要让服务器知道的信息,才应该放在 Cookie 里面

属性:

  1. Expires属性:指定一个具体的到期时间,到了指定时间以后,浏览器就不再保留这个 Cookie。它的值是 UTC 格式,可以使用Date.prototype.toUTCString()进行格式转换。
    如果不设置该属性,或者设为null,Cookie 只在当前会话(session)有效,浏览器窗口一旦关闭,当前 Session 结束,该 Cookie 就会被删除。
  2. Max-Age属性:指定从现在开始 Cookie 存在的秒数,比如60 * 60 * 24 * 365(即一年)。过了这个时间以后,浏览器就不再保留这个 Cookie。
    两者使用要点:
    如果同时指定了Expires和Max-Age,那么Max-Age的值将优先生效。
    如果Set-Cookie字段没有指定Expires或Max-Age属性,那么这个 Cookie 就是 Session Cookie,即它只在本次对话存在,一旦用户关闭浏览器,浏览器就不会再保留这个 Cookie。
  3. Domain属性:指定 Cookie 属于哪个域名,以后浏览器向服务器发送 HTTP 请求时,通过这个属性判断是否要附带某个 Cookie。未设置,默认将其设为浏览器的当前域名。
    使用要点:
    Domain 属性只能是当前域名或者当前域名的上级域名,但设为上级域名时,不能设为顶级域名或公共域名。(顶级域名指的是 .com、.net 这样的域名,公共域名指的是开放给外部用户设置子域名的域名,比如 github.io。)如果不符合上面这条规则,浏览器会拒绝设置这个 Cookie。
  4. Path属性:指定浏览器发出 HTTP 请求时,哪些路径要附带这个 Cookie。只要浏览器发现,Path属性是 HTTP 请求路径的开头一部分,就会在头信息里面带上这个 Cookie。比如,Path属性是/,那么请求/docs路径也会包含该 Cookie。当然,前提是 Domain 属性必须符合条件。
  5. Secure属性:指定浏览器只有在加密协议 HTTPS 下,才能将这个 Cookie 发送到服务器。另一方面,如果当前协议是 HTTP,浏览器会自动忽略服务器发来的Secure属性。该属性只是一个开关,不需要指定值。如果通信是 HTTPS 协议,该开关自动打开。
  6. HttpOnly属性:指定该 Cookie 无法通过 JavaScript 脚本拿到,主要是document.cookie属性、XMLHttpRequest对象和 Request API 都拿不到该属性。这样就防止了该 Cookie 被脚本读到,只有浏览器发出 HTTP 请求时,才会带上该 Cookie。
  7. SameSite属性:Chrome 51 开始浏览器新增的,用来防止 CSRF 攻击和用户追踪。
    (Cookie 往往用来存储用户的身份信息,恶意网站可以设法伪造带有正确 Cookie 的 HTTP 请求,这就是 CSRF 攻击)
    设置三个值:
    • Strict :最为严格,完全禁止第三方 Cookie,跨站点时,任何情况下都不会发送 Cookie。换言之,只有当前网页的 URL 与请求目标一致,才会带上 Cookie。
    • Lax:规则稍稍放宽,大多数情况也是不发送第三方 Cookie,但是导航到目标网址的 Get 请求除外。
      在这里插入图片描述
    • None:Chrome 计划将Lax变为默认设置。这时,网站可以选择显式关闭SameSite属性,将其设为None。不过,前提是必须同时设置Secure属性(Cookie 只能通过 HTTPS 协议发送),否则无效。

浏览器端操作cookie:

document.cookie属性用于读写当前网页的 Cookie。读取的时候,它会返回当前网页的所有 Cookie,前提是该 Cookie 不能有HTTPOnly属性。

document.cookie // “foo=bar;baz=bar”

上面代码从document.cookie一次性读出两个 Cookie,它们之间使用分号分隔。必须手动还原,才能取出每一个 Cookie 的值。

 var cookies = document.cookie.split(';');

for (var i = 0; i < cookies.length; i++) {
  console.log(cookies[i]);
}
// foo=bar
// baz=bar

document.cookie写入 Cookie 的例子如下。

document.cookie = 'fontSize=14; '
  + 'expires=' + someDate.toGMTString() + '; '
  + 'path=/subdirectory; '
  + 'domain=*.example.com';

Cookie 的属性一旦设置完成,就没有办法读取这些属性的值。

删除一个现存 Cookie 的唯一方法,是设置它的expires属性为一个过去的日期。


Web storage API

概述

Web 存储需要更加的安全与快速. 数据不会被保存在服务器上,这些数据只用于用户请求网站数据上.它也可以存储大量的数据,而不影响网站的性能.
分为两个对象:localStorage(用于长久保存整个网站的数据,保存的数据没有过期时间,直到手动去除); sessionStorage(用于临时保存同一窗口(或标签页)的数据,在关闭窗口或标签页之后将会删除这些数据)。

属性

不管是 localStorage,还是 sessionStorage,可使用的API都相同,常用的有如下几个(以localStorage为例):

  • 保存数据:localStorage.setItem(key,value);
  • 读取数据:localStorage.getItem(key);
  • 删除单个数据:localStorage.removeItem(key);
  • 删除所有数据:localStorage.clear();
  • 得到某个索引的key:localStorage.key(index);
  • 返回保存的数据项个数 localStorage.length;

Storage事件

window.addEventListener('storage', onStorageChange);

监听函数接受一个event实例对象作为参数。这个实例对象继承了 StorageEvent 接口,有几个特有的属性,都是只读属性。

  • StorageEvent.key:字符串,表示发生变动的键名。如果 storage 事件是由clear()方法引起,该属性返回null。
  • StorageEvent.newValue:字符串,表示新的键值。如果 storage 事件是由clear()方法或删除该键值对引发的,该属性返回null。
  • StorageEvent.oldValue:字符串,表示旧的键值。如果该键值对是新增的,该属性返回null。
  • StorageEvent.storageArea:对象,返回键值对所在的整个对象。也说是说,可以从这个属性上面拿到当前域名储存的所有键值对。
  • StorageEvent.url:字符串,表示原始触发 storage 事件的那个网页的网址。

具体例子:

 function onStorageChange(e) {
  console.log(e.key);
}

window.addEventListener('storage', onStorageChange);

注意,该事件有一个很特别的地方,就是它不在导致数据变化的当前页面触发,而是在同一个域名的其他窗口触发。也就是说,如果浏览器只打开一个窗口,可能观察不到这个事件。比如同时打开多个窗口,当其中的一个窗口导致储存的数据发生改变时,只有在其他窗口才能观察到监听函数的执行。可以通过这种机制,实现多个窗口之间的通信。

IndexedDB

概述:

随着浏览器的功能不断增强,越来越多的网站开始考虑,将大量数据储存在客户端,这样可以减少从服务器获取数据,直接从本地获取数据。

现有的浏览器数据储存方案,都不适合储存大量数据:Cookie 的大小不超过 4KB,且每次请求都会发送回服务器;LocalStorage 在 2.5MB 到 10MB 之间(各家浏览器不同),而且不提供搜索功能,不能建立自定义的索引。所以,需要一种新的解决方案,这就是 IndexedDB 诞生的背景。

通俗地说,IndexedDB 就是浏览器提供的本地数据库,它可以被网页脚本创建和操作。IndexedDB 允许储存大量数据,提供查找接口,还能建立索引。这些都是 LocalStorage 所不具备的。就数据库类型而言,IndexedDB 不属于关系型数据库(不支持 SQL 查询语句),更接近 NoSQL 数据库。

特点:

  1. 键值对储存。 IndexedDB 内部采用对象仓库(object store)存放数据。所有类型的数据都可以直接存入,包括 JavaScript 对象。对象仓库中,数据以“键值对”的形式保存,每一个数据记录都有对应的主键,主键是独一无二的,不能有重复,否则会抛出一个错误。
  2. 异步。 IndexedDB 操作时不会锁死浏览器,用户依然可以进行其他操作,这与 LocalStorage 形成对比,后者的操作是同步的。异步设计是为了防止大量数据的读写,拖慢网页的表现。
  3. 支持事务。 IndexedDB 支持事务(transaction),这意味着一系列操作步骤之中,只要有一步失败,整个事务就都取消,数据库回滚到事务发生之前的状态,不存在只改写一部分数据的情况。
  4. 同源限制。 IndexedDB 受到同源限制,每一个数据库对应创建它的域名。网页只能访问自身域名下的数据库,而不能访问跨域的数据库。
  5. 储存空间大。 IndexedDB 的储存空间比 LocalStorage 大得多,一般来说不少于 250MB,甚至没有上限。
  6. 支持二进制储存。 IndexedDB 不仅可以储存字符串,还可以储存二进制数据(ArrayBuffer 对象和 Blob 对象)。

基本概念

  1. 数据库:IDBDatabase 对象(数据库是一系列相关数据的容器。每个域名(严格的说,是协议 + 域名 + 端口)都可以新建任意多个数据库。)
    注意:ndexedDB 数据库有版本的概念。同一个时刻,只能有一个版本的数据库存在。如果要修改数据库结构(新增或删除表、索引或者主键),只能通过升级数据库版本完成。
  2. 对象仓库:IDBObjectStore 对象(每个数据库包含若干个对象仓库(object store)。它类似于关系型数据库的表格。)
  3. 索引: IDBIndex 对象(为了加速数据的检索,可以在对象仓库里面,为不同的属性建立索引。)
  4. 事务: IDBTransaction 对象(数据记录的读写和删改,都要通过事务完成。事务对象提供error、abort和complete三个事件,用来监听操作结果。)
  5. 操作请求:IDBRequest 对象
  6. 指针: IDBCursor 对象
  7. 主键集合:IDBKeyRange 对象
  8. 数据记录(对象仓库保存的是数据记录。每条记录类似于关系型数据库的行,但是只有主键和数据体两部分。数据体可以是任意数据类型,不限于对象。主键用来建立默认的索引,必须是不同的,否则会报错。主键可以是数据记录里面的一个属性,也可以指定为一个递增的整数编号。)

操作流程

  1. 打开数据库
//open方法这个方法接受两个参数,第一个参数是字符串,表示数据库的名字。如果指定的数据库不存在,就会新建数据库。第二个参数是整数,表示数据库的版本。如果省略,打开已有数据库时,默认为当前版本;新建数据库时,默认为1
//indexedDB.open()方法返回一个 IDBRequest 对象。这个对象通过三种事件error、success、upgradeneeded,处理打开数据库的操作结果。
 var request = indexedDB.open('test',1);
        var db;
        request .onsuccess=function(e){
            console.log('打开成功')
            db = request .result;//通过request对象的result属性拿到数据库对象
        }
        request .onerror=function(e){
            console.log('打开失败')
        }
        request .onupgradeneeded=function(e){//如果指定的版本号,大于数据库的实际版本号,就会发生数据库升级事件
            console.log('gradeneeding');
            db = event.target.result;//通过事件对象的target.result属性,拿到数据库实例。
        } 
  1. 新建数据库

新建数据库与打开数据库是同一个操作。如果指定的数据库不存在,就会新建。不同之处在于,后续的操作主要在upgradeneeded事件的监听函数里面完成,因为这时版本从无到有,所以会触发这个事件。

request.onupgradeneeded = function (e) {//需要升级
            console.log('gradeneeding');
            db = e.target.result;
            console.log(db);
            console.log(db.objectStoreNames);
            if (!db.objectStoreNames.contains('person')) {//判断表格是否存在
                var objectStore = db.createObjectStore('person', { keypath: 'id' });//数据库新建成功以后,新增一张叫做person的表格,主键是id
                objectStore.createIndex('name', 'name', { unique: false });
                objectStore.createIndex('email', 'email', { unique: true });//IDBObject.createIndex()的三个参数分别为索引名称、索引所在的属性、配置对象(说明该属性是否包含重复的值)。
            }
            //说明:主键(key)是默认建立索引的属性。比如,数据记录是{ id: 1, name: '张三' },那么id属性可以作为主键。主键也可以指定为下一层对象的属性,比如{ foo: { bar: 'baz' } }的foo.bar也可以指定为主键。
            //var objectStore = db.createObjectStore('person',{ autoIncrement: true }); //如果数据记录里面没有合适作为主键的属性,指定主键为一个递增的整数。

        }
  1. 新增数据
  function add() {
            //写入数据需要新建一个事务。新建时必须指定表格名称和操作模式(“只读”或“读写”)。
            //新建事务以后,通过IDBTransaction.objectStore(name)方法,拿到 IDBObjectStore 对象,再通过表格对象的add()方法,向表格写入一条记录。
            var trans = dbRes.transaction(['person'], 'readwrite').objectStore('person').add({ id: 1, name: '张三', age: 24, email: 'zhangsan@example.com' });
            trans.onsuccess = function (event) {
                console.log('数据写入成功');
            };

            trans.onerror = function (event) {
                console.log('数据写入失败');
            }
        };
  1. 读取数据

```javascript
  function read() {
            var trans = dbRes.transaction(['person']).objectStore('person').get(1);//objectStore.get()方法用于读取数据,参数是主键的值。
            trans.onerror = function (event) {
                console.log('事务失败');
            };

            trans.onsuccess = function (event) {
                if (trans.result) {
                    console.log('Name: ' + trans.result.name);
                    console.log('Age: ' + trans.result.age);
                    console.log('Email: ' + trans.result.email);
                } else {
                    console.log('未获得数据记录');
                }
            };
        }
  1. 读取数据
 function readAll() {
            var objectStore = dbRes.transaction(['person']).objectStore('person');
            objectStore.openCursor.onsuccess = function (e) {//新建指针对象的openCursor()方法是一个异步操作,所以要监听success事件。
                var cursor = e.target.result;
                console.log(cursor)
                if (cursor) {
                    console.log('Id: ' + cursor.key);
                    console.log('Name: ' + cursor.value.name);
                    console.log('Age: ' + cursor.value.age);
                    console.log('Email: ' + cursor.value.email);
                    cursor.continue();
                } else {
                    console.log('没有更多数据了!');
                }

            }

        }
  1. 更新数据(增加数据)
function update() {
            var request = dbRes.transaction(['person'], 'readwrite')
                .objectStore('person')
                .put({ id: 2, name: '李四', age: 35, email: 'lisi@example.com' });

            request.onsuccess = function (event) {
                console.log('数据更新成功');
            };

            request.onerror = function (event) {
                console.log('数据更新失败');
            }
        }

21.删除数据

  function remove() {
            var request = dbRes.transaction(['person'], 'readwrite')
                .objectStore('person')
                .delete(1);

            request.onsuccess = function (event) {
                console.log('数据删除成功');
            };
        }

22.通过索引找数据

  function indexRead() {
            var transaction = dbRes.transaction(['person'], 'readonly').objectStore('person').index('name').get('李四');
            transaction.onsuccess = function (e) {
                var result = e.target.result;
                if (result) {
                    console.log('name李四', result);
                    // ...
                } else {
                    // ...
                }
            }
        }

indexedDB 对象

  • indexedDB.open()方法用于打开数据库。这是一个异步操作,但是会立刻返回一个 IDBOpenDBRequest 对象。
 var openRequest = window.indexedDB.open('test', 1);

上面代码表示,打开一个名为test、版本为1的数据库。如果该数据库不存在,则会新建该数据库。

open()方法的第一个参数是数据库名称,格式为字符串,不可省略;第二个参数是数据库版本,是一个大于0的正整数(0将报错),如果该参数大于当前版本,会触发数据库升级。第二个参数可省略,如果数据库已存在,将打开当前版本的数据库;如果数据库不存在,将创建该版本的数据库,默认版本为1。
打开数据库是异步操作,通过各种事件通知客户端。下面是有可能触发的4种事件。

success:打开成功。
error:打开失败。
upgradeneeded:第一次打开该数据库,或者数据库版本发生变化。
blocked:上一次的数据库连接还未关闭。
第一次打开数据库时,会先触发upgradeneeded事件,然后触发success事件。

根据不同的需要,对上面4种事件监听函数。

  • indexedDB.deleteDatabase() 方法用于删除一个数据库,参数为数据库的名字。它会立刻返回一个IDBOpenDBRequest对象,然后对数据库执行异步删除。删除操作的结果会通过事件通知,IDBOpenDBRequest对象可以监听以下事件。
    success:删除成功
    error:删除报错
    调用deleteDatabase()方法以后,当前数据库的其他已经打开的连接都会接收到versionchange事件。

注意,删除不存在的数据库并不会报错。

  • indexedDB.cmp() 方法比较两个值是否为 indexedDB 的相同的主键。它返回一个整数,表示比较的结果:0表示相同,1表示第一个主键大于第二个主键,-1表示第一个主键小于第二个主键。
window.indexedDB.cmp(1, 2) // -1
window.indexedDB.cmp(1, true) // 报错
window.indexedDB.cmp({}, {}) // 报错

注意,这个方法不能用来比较任意的 JavaScript 值。如果参数是布尔值或对象,它会报错。

Web Sql

概述

Web Sql 属于关系型数据库,可以直接通过sql语句进行查询。

核心方法

openDatabase:这个方法使用现有的数据库或者新建的数据库创建一个数据库对象。
transaction:这个方法让我们能够控制一个事务,以及基于这种情况执行提交或者回滚。
executeSql:这个方法用于执行实际的 SQL 查询。

简单使用

   var db = openDatabase('mydb', '1.0', 'Test DB', 2 * 1024 * 1024);//(openDatabase(数据库名称,版本号,描述文本,数据库大小,创建回调),第五个参数,创建回调会在创建数据库后被调用。)
    var e_id = 3, e_log = 'sweet';
    db.transaction(function (tx) {
        tx.executeSql('CREATE TABLE IF NOT EXISTS LOGS (id unique, log)');//创建名为LOGS数据库
        tx.executeSql('INSERT INTO LOGS (id, log) VALUES (1, "hello world")');//插入数据
        tx.executeSql('INSERT INTO LOGS (id, log) VALUES (2, "yiyayiya")');
        tx.executeSql('INSERT INTO LOGS (id, log) VALUES (?, ?)', [e_id, e_log]);//动态插入数据
    });
    //遍历数据
    db.transaction(function (tx) {
        tx.executeSql('SELECT * FROM LOGS', [], function (tx, results) {
            var len = results.rows.length, i;
            console.log('共有数据' + len);

            for (i = 0; i < len; i++) {
                console.log(results.rows.item(i).log);
            }

        }, null);
    });
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值