cookie、localStorage、sessionStroage介绍及其对比

1,309 阅读10分钟

1. cookie

cookie最初是设计给服务端用的。如果客户端有cookie,则每次请求都会发给服务端。下面来具体看一下这个机制。

1.1 cookie 的设置

就像上面说的,cookie最初是设计给服务端使用的。cookie就像服务端给这个客户端加的一个标签,当客户端再次请求时,会带着这个标签,那么服务器就能通过携带的cookie知道当前的客户端是谁了。

cookie的设置过程大体如下:

  1. 客户端请求服务端,服务端看到客户端是新来的,就用回复头里的set-cookie设置一个cookie给这个客户端返回,作为标记,例如这个标签是 Joey
  2. 客户端收到cookie,在以后向服务端再发请求的时候会在头部的cookie字段加上服务端在上一步中发来的cookie,即 Joey
  3. 服务端收到了cookie: Joey, 就知道了这个用户是Joey, 然后就可以做进一步的操作了

1.2 cookie 的用处

Cookie主要用于以下三个方面:

  1. 会话状态管理(如用户登录状态、购物车、游戏分数或其它需要记录的信息)

  2. 个性化设置(如用户自定义设置、主题等)

  3. 浏览器行为跟踪(如跟踪分析用户行为等)

1.3 安全问题--cookie 是不可信的

cookie 是可以被修改的,客户端和服务端都可以对已经设置的cookie进行修改。

在1.1部分的cookie设置过程中可知,如果客户端将本来是 Joey的 cookie 改成 Ross,那么服务器就会认为现在发请求的客户端是 Ross,可能会给客户端 Joey 返回本来属于 Ross 的内容。这肯定是不应该的。

所以说, cookie 是不可信的。

1.4 cookie 的体积限制

客户端在获得服务端返回的 cookie 后,在以后再给服务器发请求时,无论服务端是不是想要 cookie, 都会放在请求里发过去。

我们知道,网络请求是非常耗时间的,如果cookie过大,每次请求服务器时都会在 header 中带上 cookie ,则网络请求时间会过长。

所以大多数浏览器都对每个域的cookie数量和大小有限制,每个浏览器对cookie数量的限制不相同,但对总大小的限制大都是4096B。

如果新加入的cookie要超过了浏览器的最大数量限制,会删除较早的 cookie 以腾出空间。不同浏览器的删除策略不同,有的会删除最近最少使用的 cookie,例如 IE和Opera;有些似乎会随机选择一个来删除,例如 Firefox...

如果新的加入新的 cookie 之后所有 cookie 的总大小超过限制,则新的 cookie 不会设置成功, 会被丢弃。

所以,不要设置太多 cookie, 以免出现意料不到的事情。

1.5 cookie 的构成

cookie由以下几个字段组成:

  1. name:名称,唯一值,用来唯一识别cookie,不分大小写。必须经过 URL 编码
  2. value:值,对应于名称,必须经过 URL 编码
  3. domain:域,设置cookie对于哪个域是有效的,都向这个域里发cookie。例如domain值为www.baidu.com,则所有发向这个网址的请求,都会发这个cookie
  4. path:路径,指定域里的路径可以接受这个cookie,例如百度下的/search路径:www.baidu.com/search
  5. expires:过期时间,表示这个cookie什么时候应该被删除,这是个日期格式的。如果没有定义,cookie会在对话结束时过期。如果想让一个cookie马上过期,则可以将这个值设置成过去的时间。
  6. max-age:过期倒计时,以秒为单位。如果设置成0,则这个cookie立刻失效,设置负数时不会产生cookie文件
  7. secure :设置该 cookie 只通过https协议传输,这不是键值对,是个标记,写上就代表设置为只在使用 https 传输。从 Chrome 52 和 Firefox 52 开始,不安全的站点使用http协议的站点无法使用Cookie的 Secure 标记
  8. http-only:设置不允许在浏览器端用 JavaScript 获取到 cookie。这也不是个键值对,是个标记,写上就代表生效。可以用来防止跨域脚本(XSS )攻击
  9. samesite:要求cookie在跨站请求时不会被发送,防止跨站请求伪造(CSRF)的特性,可取值

其中除name: value为必须之外,其余都是可选项。

1.6 在客户端用 JavaScript 操作 cookie

在客户端使用 JavaScript 对 cookie 的操作可以分为两类,分别是读取 cookie 和 设置 cookie,下面分别进行讨论。

BOM 的提供的操作 cookie 的 api 看起来有些难用。

1.6.1 读取 cookie

使用 document.cookie 即可获得该页面中所有的 cookie,例如,我们来看看百度学术给我设置了什么cookie。打开http://xueshu.baidu.com/。按F12,打开上部分的 Application 选项卡,然后打开左侧的 cookies。

百度学术在我的电脑里保存的cookie如图:

然后在命令台上通过 document.cookie 来获取页面里的 cookie:

// 获取 cookie, 得到的是所有 cookie 组成的字符串
let cookies = document.cookie;
// 提取出所有的 cookie 到数组中
let cookieArray = cookies.split(';');
// 用 console.table 输出cookie数组,这样比较清晰
console.table(cookieArray);

结果如下:

可以看到通过 document.cookie 获取的 cookie 的 name 和 value 与浏览器工具得到的相同。

以图中第一个cookie为例,cookie 名为 PSTM , value 是1563885xxx,域名是 .baidu.com,过期时间是2087年8月10号,其他属性都取默认值。

1.6.2 设置 cookie

给 document.cookie 赋一个新的 cookie 字符串可以设置一个新的 cookie 了。

以MDN为例,在这个网站域名下设置一个自己的cookie,配置如下:

  • cookie 名为 username,
  • cookie 值为 test,
  • domain为 .mozilla.org
  • path为 /

其他选项不设置,也就是取默认值。

设置之前的 cookie 为:

设置:

let name = 'username',
	value = 'test',
	domain = '.mozilla.org',
	path = '/';

var myCookie = encodeURIComponent(name) + '=' + encodeURIComponent(value) + ';domain=' + domain + ';path=' + path;

document.cookie = myCookie;

结果如下:

可以看到 cookie 设置成功了。

1.7 服务端设置 cookie 发送给客户端

由于不同服务端程序的在响应报文的头部设置 set-cookie 操作的具体代码不同,这里以 node.js 的 koa 框架为例。

先起一个极简的 server,然后对于访问者返回的报文的 header 都设置 set-cookie 头部:

const Koa = require('koa');
const app = new Koa();

app.use(async ctx => {
  ctx.cookies.set('koa_cookie_name', 'koa_cookie_value');
  ctx.body = 'cookie setted';
});

app.listen(3001, (e) => {
    console.log('server listening port 3001');
});

进入'http://localhost:3001/'后去查看 cookie:

可以看到服务端传来的 cookie。

2. sessionStorage 和 localStorage

sessionStorage 和 localStorage 有与cookie不同的两点:

  1. 存储的数据被严格限制在本地
  2. 不会每次向服务器请求时都带上存储的数据

2.1 总述

sessionStorage 和 localStorage 都是 Storage 对象;都是以名值对的形式存储数据,存储数据的key和value都是字符串(string)类型,如果传入的不是字符串类型,会被转换为字符串类型。

所以,如果想把非字符串类型的数据存进来,要预先手动进行编码,取出来后要再进行解码。

2.2 增删改查数据的方法

这两类对象有着相同的增删改查数据的 api,所以可以用其中的一个对象为例子,来演示这些 api,这里选择 sessionStorage :

  1. setItem(key, value):将一个键值对的保存,如果两次保存的key相同但是value不同,则后面的覆盖前面的。
  2. getItem(key):获取指定 key 对应的 value
  3. removeItem(key):删除指定的 key 及其 value
  4. key(index):获取指定位置的...
  5. clear():删除所有数据

还有一个属性:

  • length属性:判断存了多少名值对,上面key(index)方法中的参数 index 的范围是 0 =< index <= length - 1

使用sessionStorage来演示上面的 api:

存入两个键值对,使用 setItem(key, value)方法:

sessionStorage.setItem('key 1', 'value 1');
sessionStorage.setItem('key 2', 'value 2');

打开浏览器页面的开发者工具,点击Application选项卡,点击左侧的 session Storage,就能看到两条刚刚保存的键值对了。

获取两个键对应的数据,使用 getItem(key)方法:

let value1 = sessionStorage.getItem('key 1');
let value2 = sessionStorage.getItem('key 2');

console.log(value1, value2); // value 1 value 2

删除 'key 1'和其对应的数据,使用 removeItem(key)方法:

sessionStorage.removeItem('key 1');

从图中可以看到存储的数据只剩下 key 2: value 2 了。

现在再添加几条数据:

sessionStorage.setItem('key 3', 'value 3');
sessionStorage.setItem('key 4', 'value 4');
sessionStorage.setItem('key 5', 'value 5');
sessionStorage.setItem('key 6', 'value 6');

得到:

输出现在存储的数据条数,也就是键值对的条数,并输出最后一条,使用length属性:

let key_value_count = sessionStorage.length;
console.log('现在存储了 ' + key_value_count + ' 条数据'); //  现在存储了 5 条数据

一次性删除所有数据,使用 clear()方法:

sessionStorage.clear();

结果:

plus: 使用对象属性语法设置与访问数据

由于 Storage 本身就是对象,所以也可以使用一般对象的读取与设置属性的语法来存取对象,本质就是通过设置属性和值的键值对来添加与读取数据。

存取的方式也和普通对象一样有两种,分别是点号法和中括号语法,例如下面的例子:

sessionStorage.key1 = 'value 1';
console.log(sessionStorage.key1);

sessionStorage['key 2'] = 'value 2';
console.log(sessionStorage['key 2']);

结果如图:

这种语法和 api 中的 setItemgetItem效果相同。

想删除一个数据时,可以使用 delete关键字,和删除普通对象属性的操作相同:

delete sessionStorage.key1;
delete sessionStorage['key 2'];

结果:

这和 removeItem的效果相同。

2.3 两个 storage 的区别

sessionStorage 和 localStorage 的区别在声明周期和作用域这两点上,具体来说如下。

  1. 在声明周期上
    • sessionStorage :
      1. 当浏览器的标签页被关闭时, 其中存的数据也会被删除。
      2. 对于 Chrome 和 Firefox 来说,浏览器如果崩溃了,重启之后的仍然会保存原来的数据。
      3. 对于一些有重新打开标签页功能的浏览器,用户在关闭标签页后 sessionStorage 中的数据仍然存在。
    • localStorage : 当用户手动清除浏览器的缓存时,它其中的数据才会被清除。
  2. 在作用域上
    • sessionStorage :不同标签页无法相互访问 sessionStorage 中保存的数据,即使这两个标签页访问的是同一个页面
    • localStorage :
      1. 访问同协议、同域名、同端口的页面共享一个 localStorage 。而且所有页面都有修改的能力。
      2. 利用上面的特点,可以使不同的标签页互相通信,这要配合下面要说的 Storage 事件来说。

2.4 storage 事件

在 Storage 里的数据被添加、修改、删除时,会触发 storage 事件,可以在 window 对象上添加事件监听程序 onstorage 来监听事件,并且可以获取事件对象。

storage 事件对象上有如下几个属性:

  1. key: 设置或删除的键名
  2. storageArea: localStorage 或 sessionStorage
  3. newValue: 修改后的值,若为删除操作则为 null
  4. oldValue: 被更改之前的值
  5. url: 所在的地址

注意:localStorage 的事件采用的是广播机制,正在访问相同站点的页面都会受到信息。这样可以完成一定的标签页之间的通信。

参考:

MDN

《JavaScript高级程序设计》

《JavaScript权威指南》