存储

随着 Web 应用程序的出现,也产生了对于能够直接在客户端上存储用户信息能力的要求。想法很 合乎逻辑,属于某个特定用户的信息应该存在该用户的机器上。无论是登录信息、偏好设定或其他数据, Web 应用提供者发现他们在找各种方式将数据存在客户端上。

在网页开发中,存储数据有以下几个方式:

  • variable ( 存储在临时变量中,页面关闭失效)
  • cache ( 浏览器对文件的自动缓存 )
  • cookie ( 浏览器和服务端都可以设置失效时间 )
  • session ( 浏览器关闭失效 )
  • loaclStorage ( 只要不清空都有效 )
  • indexdb ( ... 结构化数据存储使用 )

HTTP Cookie,通常直接叫做 cookie,最初是在客户端用于存储会话信息的。该标准要求服务器对 任意 HTTP 请求发送 Set-Cookie HTTP 头作为响应的一部分,其中包含会话信息。

例如,这种服务器响 应的头可能如下:

HTTP/1.1 200 OK
Content-type: text/html
Set-Cookie: name=value
Other-header: other-header-value

这个 HTTP 响应设置以 name 为名称、以 value 为值的一个 cookie,名称和值在传送时都必须是 URL 编码的。浏览器会存储这样的会话信息,并在这之后,通过为每个请求添加 Cookie HTTP 头将信 息发送回服务器,如下所示:

GET /index.html HTTP/1.1
Cookie: name=value
Other-header: other-header-value

发送回服务器的额外信息可以用于唯一验证客户来自于发送的哪个请求。

cookie 由浏览器保存的以下几块信息构成:

  • 名称:一个唯一确定 cookie 的名称。cookie 名称是不区分大小写的,所以 myCookie 和 MyCookie 被认为是同一个 cookie。然而,实践中最好将 cookie 名称看作是区分大小写的,因为某些服务器会这样处理 cookie。cookie 的名称必须是经过 URL 编码的。

  • 值:储存在 cookie 中的字符串值。值必须被 URL 编码。

  • 域:cookie 对于哪个域是有效的。所有向该域发送的请求中都会包含这个 cookie 信息。这个值可以包含子域(subdomain,如 www.wrox.com),也可以不包含它(如.wrox.com,则对于 wrox.com的所有子域都有效)。如果没有明确设定,那么这个域会被认作来自设置 cookie 的那个域。

  • 路径:对于指定域中的那个路径,应该向服务器发送 cookie。例如,你可以指定 cookie 只有从 http://www.wrox.com/books/ 中才能访问,那么 http://www.wrox.com 的页面就不会发送 cookie 信息,即使请求都是来自同一个域的。

  • 失效时间:表示 cookie 何时应该被删除的时间戳(也就是,何时应该停止向服务器发送这个 cookie)。默认情况下,浏览器会话结束时即将所有 cookie 删除;不过也可以自己设置删除时间。 这个值是个 GMT 格式的日期(Wdy, DD-Mon-YYYY HH:MM:SS GMT),用于指定应该删除 cookie 的准确时间。因此,cookie 可在浏览器关闭后依然保存在用户的机器上。如果你设置的失 效日期是个以前的时间,则 cookie 会被立刻删除。

  • 安全标志:指定后,cookie 只有在使用 SSL 连接的时候才发送到服务器。例如,cookie 信息只 能发送给 https://www.wrox.com,而 http://www.wrox.com 的请求则不能发送 cookie。secure 标志是 cookie 中唯一一个非名值对儿的部分,直接包含一个 secure 单词。因为设置了 secure 标志,这个 cookie 只能通过 SSL 连接才能传输。

每一段信息都作为 Set-Cookie 头的一部分,使用分号加空格分隔每一段,如下例所示。

HTTP/1.1 200 OK
Content-type: text/html
Set-Cookie: name=value; expires=Mon, 22-Jan-07 07:10:24 GMT; domain=.wrox.com Other-header: other-header-value

该头信息指定了一个叫做 name 的 cookie,它会在格林威治时间 2007 年 1 月 22 日 7:10:24 失效,同 时对于 www.wrox.com 和 wrox.com 的任何子域(如 p2p.wrox.com)都有效。

JavaScript 中读写 cookie 不是非常直观,常常需要写一些函数来简化 cookie 的功能。基本的 cookie 操作有三种:读取、写入和删除。

var CookieUtil = {
  get: function (name){
    var cookieName = encodeURIComponent(name) + "=",
      cookieStart = document.cookie.indexOf(cookieName),
      cookieValue = null;
    if (cookieStart > -1){
      var cookieEnd = document.cookie.indexOf(";", cookieStart);
      if (cookieEnd == -1){
        cookieEnd = document.cookie.length;
      }
      cookieValue = decodeURIComponent(document.cookie.substring(cookieStart + cookieName.length, cookieEnd));
    }
    return cookieValue;
  },
  set: function (name, value, expires, path, domain, secure) {
    var cookieText = encodeURIComponent(name) + "=" + encodeURIComponent(value);
    if (expires instanceof Date) {
      cookieText += "; expires=" + expires.toGMTString();
    }
    if (path) {
      cookieText += "; path=" + path;
    }
    if (domain) {
      cookieText += "; domain=" + domain;
    }
    if (secure) {
      cookieText += "; secure";
    }
    document.cookie = cookieText;
  },
  unset: function (name, path, domain, secure){
    this.set(name, "", new Date(0), path, domain, secure);
  }
}

CookieUtil.get()方法根据 cookie 的名字获取相应的值。它会在 document.cookie 字符串中查 找 cookie 名加上等于号的位置。如果找到了,那么使用 indexOf()查找该位置之后的第一个分号(表 示了该 cookie 的结束位置)。如果没有找到分号,则表示该 cookie 是字符串中的最后一个,则余下的字 符串都是 cookie 的值。该值使用 decodeURIComponent()进行解码并最后返回。如果没有发现 cookie, 则返回 null。

CookieUtil.set()方法在页面上设置一个 cookie,接收如下几个参数:cookie 的名称,cookie 的值, 可选的用于指定 cookie 何时应被删除的 Date 对象,cookie 的可选的 URL 路径,可选的域,以及可选的 表示是否要添加 secure 标志的布尔值。参数是按照它们的使用频率排列的,只有头两个是必需的。在 这个方法中,名称和值都使用 encodeURIComponent()进行了 URL 编码,并检查其他选项。如果 expires 参数是 Date 对象,那么会使用 Date 对象的 toGMTString()方法正确格式化 Date 对象,并添加到 expires 选项上。方法的其他部分就是构造 cookie 字符串并将其设置到 document.cookie 中。

没有删除已有 cookie 的直接方法。所以,需要使用相同的路径、域和安全选项再次设置 cookie,并 将失效时间设置为过去的时间。CookieUtil.unset()方法可以处理这种事情。它接收 4 个参数:要删 除的 cookie 的名称、可选的路径参数、可选的域参数和可选的安全参数。 这些参数加上空字符串并设置失效时间为 1970 年 1 月 1 日(初始化为 0ms 的 Date 对象的值),传 给 CookieUtil.set()。这样就能确保删除 cookie。

可以像下面这样使用上述方法:

//设置 cookie
CookieUtil.set("name", "Nicholas");
CookieUtil.set("book", "Professional JavaScript");

//读取 cookie 的值
console.log(CookieUtil.get("name")); //"Nicholas" 
console.log(CookieUtil.get("book")); //"Professional JavaScript"

//删除 cookie CookieUtil.unset("name");
CookieUtil.unset("book");


//设置 cookie,包括它的路径、域、失效日期
CookieUtil.set("name","Nicholas","/books/projs/","www.wrox.com", new Date("January 1, 2010"))
//删除刚刚设置的 cookie
CookieUtil.unset("name", "/books/projs/", "www.wrox.com");
//设置安全的 cookie
CookieUtil.set("name", "Nicholas", null, null, null, true);

这些方法通过处理解析、构造 cookie 字符串的任务令在客户端利用 cookie 存储数据更加简单。

  • cookie 的名称和值传送时都必须是 URL 编码

  • 浏览器每个域下的cookie总数有限制, 20~50 个,可以通过子cookie的策略绕开浏览器的单个域名下的cookie数限制

  • 浏览器对cookie的尺寸有限制,长度需要控制在 4095B 内。

  • cookie的名称不区分大小写

  • 在设置 cookie 中,如果同时指定了Expires和Max-Age,Max-Age 将优先生效

  • document.cookie 获取页面所有 cookie

sessionStorage

sessionStorage 提供一种在 cookie 之外存储会话数据的途径。

sessionStorage 对象存储特定于某个会话的数据,也就是该数据只保持到浏览器关闭。这个对象就像会话 cookie,也会在浏览器关闭后消失。存储在 sessionStorage 中的数据可以跨越页面刷新而 6 存在,同时如果浏览器支持,浏览器崩溃并重启之后依然可用(Firefox 和 WebKit 都支持,IE 则不行)。

因为 seesionStorage 对象绑定于某个服务器会话,所以当文件在本地运行的时候是不可用的。存储在 sessionStorage 中的数据只能由最初给对象存储数据的页面访问到,所以对多页面应用有限制。

由于 sessionStorage 对象其实是 Storage 的一个实例,所以可以使用 setItem()或者直接设置新的属性来存储数据。

下面是这两种方法的例子:

// 设置
//使用方法存储数据
sessionStorage.setItem("name", "Nicholas");
//使用属性存储数据
sessionStorage.book = "Professional JavaScript";

// 读取
//使用方法读取数据
var name = sessionStorage.getItem("name");
//使用属性读取数据
var book = sessionStorage.book;

localStorage

localStorage 提供一种存储大量可以跨会话存在的数据的机制。

要访问同一个 localStorage 对象,页面必须来自同一个域名(子域名无效),使用同一种 协议,在同一个端口上。

下面是一些例子:

//使用方法存储数据
localStorage.setItem("name", "Nicholas");
//使用属性存储数据
localStorage.book = "Professional JavaScript";
//使用方法读取数据
var name = localStorage.getItem("name");
//使用属性读取数据
var book = localStorage.book;

参考: