今天看啥  ›  专栏  ›  野茂痒呆

129. 周五了

野茂痒呆  · 简书  ·  · 2021-04-23 23:20

---

title: 前端存储简介

tags: js

---

几年前,应项目经理要求写的一篇文章,还发表在公司内部的《项目通讯》的杂志上。长时间不看不用,要是让我再来介绍一下前端的存储机制,恐怕我只能记得起有 Cookie、sessionStorage 几个简单的名词。温故而知新啊!

<!--more-->

摘要:介绍目前客户端存储的几种形式,并绕过后台代码,通过客户端JavaScript的方式提供相应的代码。

关键字:前端存储  Web存储

## 一 前端存储作用

客户端存储赋予了 Web 浏览器记忆功能,Web应用可以记住用户的偏好和用户的各种信息,以便在用户再次访问时,提供更加友好的内容,提升用户的体验。客户端存储遵循“同源策略”,不同站点无法相互读取对方的数据,同一站点可以共享存储数据,它提供了一种站点内的通信机制,客户端存储是针对个人或者设备的,同一站点下不同设备,甚至同一设备不同浏览器之间数据是独立各异的。Web存储也可以设置有效期,可以根据时效性来保留或者删除存储数据。

## 二 前端存储形式

### 1. Cookie

cookie最早是设计为服务端所用的。这里介绍通过前端脚本 Document 对象的 cookie 属性对 cookie 进行操作。

cookie 的常用属性有 name, value, expires/max-age, path, domain, secure。

name 和 value 即为 cookie 的名称和值,expires(一般是绝对时间,指定到某天)和 max-age(相对时间,单位是秒)是 cookie 的有效期。

默认情况下,cookie 和创建它的 web 页面有关,并对该页面以及和该页面同目录或子目录的其他 web 页面可见。如果要让其他目录下的页面也可见,需要设置 path,如path = "/" 则是让同域名下所有页面可见。

domain 用来设置跨域,如果要 x.example.com 和y.example.com 共享 cookie,那么在设置 cookie 时,设置 domain 属性为 ".example.com" 即可实现。

secure 用来表明 cookie 的值以何种形式通过网络传递,该属性不是键值对的形式,是没有值的独立属性。cookie 默认是不安全的,而一旦添加该属性,那就只能当浏览器和服务器通过 https 或者其他安全协议连接时才能传递。

因为 cookie 的名/值不允许分号逗号等字符,因此存储前一般用 encodeURIComponent() 对值进行编码,相应的,读取的时候用 decodeURIComponent() 函数解码。另外 cookie 保存的每条数据不能超过 4K,每个浏览器对存放的 cookie 条数也有限制。

以下是郑晓明同学写的对 cookie 的增删和读取。

```javascript

/*

* name: Cookie属性名

* value:Cookie属性值

* expiresHours:cookie保存时间,单位:小时,>0有效

*/

function addCookie(name, value, expiresHours){

var cookieString = name + '=' + encodeURIComponent(value);

if (expiresHours > 0) {

var date = new Date();

date.setTime(date.getTime() + expiresHours * 3600 * 1000);

cookieString=cookieString+';expires='+date.toUTCString()+';path=/';

}

document.cookie = cookieString;

}

function getCookie(name){

var strCookie = document.cookie;

var arrCookie = strCookie.split(';');

var i = 0, len = arrCookie.length;

for (; i < len; i++) {

var arr = arrCookie[i].split('=');

if(sanTrim(arr[0]) == name) {return decodeURIComponent(arr[1])};

}

return '';

}

function deleteCookie(name){

var date = new Date();

date.setTime(date.getTime() - 10000);

document.cookie = name + '=null; expires=' + date.toUTCString();

}

```

### 2. Web存储

Web存储标准所描述的 API 包含 localStorage 对象和 sessionStorage 对象。这两个对象都是持久化关联数组,是键值对的映射表。根据浏览器的不同,存储的数据在2.5MB 到 10MB 之间。数组使用字符串来索引,存储的值也都是字符串形式的。localStorage 和 sessionStorage 一般被当做普通的 JavaScript 对象使用,两者 API 是一样的。

```javascript

// localStorage 示例,sessionStorage 有同样的方法

localStorage.setItem(name,value);//存储数据

localStorage.getItem(name);//获取数据

localStorage.removeItem(name);//根据name删数据

localStorage.clear();//删除所有数据

// 遍历(这是几年前的写法,现在 es6 已经很流行了,当然仍然可以用)

for(var name in localStorage){

var value = localStorage[name];

}

for(var i=0;i<localStorage.length;i++){

var name = localStorage.key(i);

var value = localStorage.getItem(name);

}

```

localStorage 和 sessionStorage 的区别在于存储的有效期和作用域不同。

localStorage 存储的数据是永久性的,除非手动删去浏览器的配置,或者Web应用刻意删除存储的数据。localStorage 的作用域是限定在文档源级别的,即同域下可以共享数据。同源下的两个窗口,如果一个窗口改动了 localStorage 数据,另一个窗口也会收到。

sessionStorage 存储数据的有效期和共享范围限于当前顶级窗口,也就相当于当前标签页(包括嵌入的 iframe),一旦该标签页关闭,sessionStorage 存储的数据就会被清除;其他的标签页不能访问本标签页的存储数据,哪怕这两个标签页渲染的是同一个页面。

### 3. IE User Data

^_^  本人是十分排斥 IE 的,好在如今 IE 不再被微软支持了,这段说明权当不存在吧。

IE5 以及IE5 以上版本的浏览器是通过在 document 元素后面附加一个专属的“DHTML 行为”来实现客户端存储的。userData 仅限于 IE 浏览器。

```javascript

var memory = document.createElement(“div”);//创建一个元素

memory.id=“_memory”;//设定一个id

memory.style.display = “none”;//通过css将其隐藏

memory.style.behavior = “url(‘#default#userData’)”;//附加userData行为

document.body.appendChild(memory);//将其添加到document元素中

```

一旦给元素赋予了“userData”行为,该元素就拥有了 load() 和 save() 方法。

load() 方法用于载入存储的数据,使用它的时候必须传一个字符串作为参数,该参数相当于文件名,该参数用来指定要载入和存储的数据,通过 getAttribute() 和 setAttribute() 来获取和设置数据的属性。

```javascript

memory.load(“myStoredData”);

memory.setAttribute(name,value);

memory.save(“myStoredData”);

memory.getAttribute(name);

```

userData存储的数据默认情况下,永不失效。也可以通过设置有效期限使其过期。

```javascript

var now = (new Date()).getTime()

var expires = now + 100*24*60*60*1000;

expries = new Date(expries).toUTCString();

memory.expires = expires;

```

userData 的作用域限制在和当前文档同目录的文档中,不能设置作用域。

### 4. 离线Web应用

HTML5 中新增的“应用程序缓存”和一般的浏览器缓存不同,它不会随着用户清除浏览器缓存而被清除,它不是真正意义上的“缓存”,更像是“安装”在浏览器,除非“卸载”,否则它们会一直在那。应用程序缓存的目的是要保证能够在离线状态下依然可以访问。离线时通过 localStorage 保存相关的数据,在线的时候能够将存储的数据传到服务器。通过 navigator.onLine 属性检测浏览器是否在线。

应用程序缓存的创建需要设置 `<html>` 标签里设置 `manifest` ,并指定一个需要缓存的清单文件。

```html

<!DOCTYPE HTML>

<html manifest=”myapp.appcache”>

<head>...</head>

<body>...</body>

</html>

```

myapp.appcache 即为清单文件。清单文件的首行必须是 “CACHE MANIFEST”,其余是要缓存的 URL 列表,“#” 为注释。如以下举例内容:

```

CACHE MANIFEST

#上一行标识此文件是一个清单文件,本行是注释

#下面的内容都是应用程序一来的资源文件的URL

myapp.html

myapp.js

myapp.css

images/background.png

```

一旦一个 Web 应用首次下载下来并缓存,之后的任何加载请求都来自缓存。在线状态下,浏览器会异步检查文件清单文件是否有更新,但不检查缓存的文件是否有更新,这时如果要让修改后的缓存文件生效,最方便的做法是更新清单文件版本号:

```

CACHE MANIFEST

#myapp version 1(更新这个数字以便让浏览器重新下载这个文件)

myapp.html

myapp.js

```

如果要让 web 应用从缓存中“卸载”,须要删除清单文件。

另外,浏览器在更新缓存的过程中,会触发一系列的事件。这些事件注册在 ApplicationCache 对象上,该对象是 window 的 applicationCache 属性的值。事件如下:

* checking: 每当应用程序载入时,都会检查清单文件,触发 checking。

* noupdate: 如果清单文件没变,同时应用程序也已经缓存了,触发 noupdate 事件。

* downloading: 如果还未缓存应用程序,或者清单文件有改动,触发 downloading, 同时开始下载。

* progress: 在下载过程中间断性的触发 progress 事件。

* cached: 当下载完成并且首次将应用程序下载到缓存中时触发 cached 事件。

* updateready: 当下载完成并将缓存中的应用程序更新后,浏览器会触发 updateready 事件。

* error: 如果处于理想状态,检查列表清单失败,触发 error 事件。

* obsolete: 如果一个缓存的应用程序引用一个不存在的清单文件,触发 obsolete 事件,之后通过网络来加载资源。

通过 applicationCache.status 属性可以查看当前缓存状态。

### 5. IndexedDB

IndexedDB 就是浏览器提供的本地数据库,它可以被网页脚本创建和操作。IndexedDB 允许储存大量数据,提供查找接口,还能建立索引。

IndexedDB 内部采用对象仓库(object store)存放数据。所有类型的数据都可以直接存入,数据以"键值对"的形式保存,每一个数据记录都有对应的主键,主键是独一无二的。

IndexedDB 是异步的,操作时不会锁死浏览器,用户依然可以进行其他操作,异步设计是为了防止大量数据的读写,拖慢网页的表现。并且其支持事务(transaction),一系列操作步骤之中,只要有一步失败,整个事务就都取消,数据库回滚到事务发生之前的状态,不存在只改写一部分数据的情况。

IndexedDB 受到同源限制,每一个数据库对应创建它的域名。网页只能访问自身域名下的数据库,而不能访问跨域的数据库。它的储存空间一般来说不少于 250MB,甚至没有上限。

IndexedDB 主要有以下接口:数据库 IDBDatabase 对象、对象仓库 IDBObjectStore 对象、索引 DBIndex 对象、事务 IDBTransaction 对象、操作请求 IDBRequest 对象、指针 IDBCursor 对象、主键集合 IDBKeyRange 对象。

一般操作流程如下:

1、打开数据库

```javascript

var request = window.indexedDB.open(databaseName, version);

var db;

request.onsuccess = function (event) {

console.log('数据写入成功');

};

request.onerror = function (event) {

console.log('数据写入失败');

}

request.onupgradeneeded = function (event) {

db = event.target.result;

}

```

这个方法接受两个参数,第一个参数是字符串,表示数据库的名字。如果指定的数据库不存在,就会新建数据库。第二个参数是整数,表示数据库的版本。如果省略,打开已有数据库时,默认为当前版本;新建数据库时,默认为1。

indexedDB.open() 方法返回一个 IDBRequest 对象。这个对象通过 error、success、upgradeneeded 事件处理打开数据库的操作结果。

2、建表

建表应该在 onupgradeneeded 部分完成。

```javascript

var objectStore = db.createObjectStore('person', { keyPath: 'id' });

// 新增一张叫做person的表格,主键是id。更好的写法是先判断一下,这张表格是否存在,如果不存在再新建。

if (!db.objectStoreNames.contains('person')) {

objectStore = db.createObjectStore('person', { keyPath: 'id' });

}

```

3、增改查删

```javascript

var request = db.transaction(['person'], 'readwrite').objectStore('person');

request.add({ id: 1, name: '张三', age: 24, email: 'zhangsan@example.com' });

request.put({ id: 1, name: '李四', age: 35, email: 'lisi@example.com' });

request.get(id);

request.delete(id);

```

每个操作之后都可以监听error、success事件。

## 三 总结

前端存储的数据在浏览器上是可见的,因此敏感的信息不能通过该方式存储。各种存储方式能存储的数据大小、数据类型、有效期、及浏览器的支持程度各不相同,因此合理选择使用各种方式。




原文地址:访问原文地址
快照地址: 访问文章快照