概述
什么是客户端存储?
使用一些方法、工具(浏览器提供的API),将网页(web应用)中的数据存储到用户的电脑上。客户端存储遵循同源策略
。
web应用可以选择存储数据的有效期。
客户端存储的几种形式:
[不兼容早期浏览器
] Web存储:最初是H5 API的一部分,后来作为独立标准。Web存储API包含local storage和session storage对象
。这两个对象实际上是持久化关联数组
,是名值对的映射表,其中“名”,“值”都是字符串
。
[兼容性好
] cookie:起初是针对服务器端脚本设计使用的客户端存储机制
。提供了非常繁琐的API,只适合存储少量的文本数据。而且以cookie形式存储的数据无论服务器端是否需要,每一次HTTP请求都会把这些数据传送到服务器。而目前仍被大量使用的原因是其兼容性好。
Cookie数据会带到请求头的cookie字段里面,每次同主域名的请求中,都会传递数据,增加了网络请求的数据量,并且造成主域的污染。
[IE专属
] User Data:对于IE8以前的浏览器,可以将其用做是Web存储的替代方案。
[H5标准
] 应用程序缓存:在H5中定义的一组API,它实现的是将Web应用整体存储在客户端。H5中增加了应用程序缓存,允许web应用将本身保存到本地。不会随用户清除浏览器缓存而被清除。缓存大小不固定,会被最近一次访问的新数据代替掉。不是真正意义上的缓存。
[浏览器厂商
] Web数据库:集成客户端数据库功能。但是这类API的标准化最终失败。目前还有一种索引数据库API。
[主流浏览器
] 文件系统API:Web应用使用类似基于文件的存储机制。
客户端存储的安全性:
任何客户端存储形式存储数据都不加密
,因此任何形式的客户端存储不应该用来保存密码,商业账号。
应当使用客户端存储机制为网站提升用户体验,而不是用他们来收集和侵犯隐私相关的数据。
1.Web存储
实现了Web存储草案标准的浏览器在window对象上定义了两个属性:localStorage
和 sessionStorage
。
两个属性都代表同一个Storage对象:一个持久化关联数组
,数组使用字符串索引
,存储的值也是字符串
形式的。
两者的区别
在于存储的有效期
和作用域
不同:数据可以存储多久以及谁拥有数据的访问权。
可以存储结构化数据也可以存储原始类型数据。
localStorage.x = 20;
var x = parseInt(localStorage.x);
//存储一个日期类型时进行编码,获取的时候进行解码
localStorage.lastRead = (new Date(2018)).toUTCString;
var lastRead = new Date(Date.parse(localStorage.lastRead));
localStorage.data = JSON.stringify({a:3});
var data = JSON.parse(localStorage.data);
有效期和作用域
localStorage
存储数据的有效期是永久
性的,作用域是限定在文档源
级别的,同时受到浏览器供应商限制
(不同浏览器之间访问同一站点无法获取上次的存储数据)。同源的文档间共享localStorage数据。
localStroage的适用范围
:
localStroage有5M的容量可以存储,所以可以存储一些不需要和服务器进行交互的一些数据
。比如导航栏当前的状态,一些普通的数据进行缓存。甚至我们可以存储html片段,js或者css文件段。由于现在手机端对于localstroage的支持已经非常完善,有很多应用通过版本控制来存储一些不经常改动的js/css文件。减少用户请求带宽的同时优化整个页面的加载速度。
文档源是通过协议,主机名以及端口号确定的。
http://www.example.com //协议:http 主机号:www.example.com
https://www.example.com //协议不同
http://static.example.com //主机名不同
http://www.example.com:8000 //端口号不同
sessionStorage
存储数据的有效期与存储数据的脚本所在的最顶层窗口
或浏览器标签页
是一致的,窗口或者标签页被关闭,那么所有通过sessionStorage存储的数据将被删除。[现在浏览器具备了重新打开最近关闭的标签页随后恢复上一次浏览的会话功能。]
sessionStorage的作用域也是限定在文档源
中。同时被限定在顶级窗口
中(如果存在两个<iframe>
,它们所包含的文档是同源的),同源文档渲染在不同的标签页中,它们的数据无法共享,一个标签页中的脚本无法读取或者覆盖另一个标签页脚本写入的数据。
存储API
可以当作普通对象使用。
var name = localStorage.username;
name = localStorage["username"];
delete localStorage.username; //IE8 不支持
一些正式API:
- setItem(“name”/value):传入名值对,存储数据。
- getItem(“name”):传入名字,获取对应的值。
- removeItem(“name”):传入名字,染出对应值。
- clear():删除所有存储的数据。
- 枚举所有存储的名值对:
for(var i = 0; i < localStorage.length; i++){ //length表示了所有名值对的总数
var name = localStorage.key(i); // 获取第i对的名字
var value = localStorage.getItem(name); // 获取该对的值
}
兼容性
//识别出使用的是哪类存储机制
var memoey = window.localStorage || (window.UserDataStorage && new UserDataStorage()) || new cookieStorage();
//由于顶层API对其兼容,依旧可以使用
var username = memoey.getItem("username");
存储事件
存储在localStorage或sessionStorage的数据发生改变时,浏览器都会在其它对该数据可见的窗口对象上触发存储事件(在对数据改变的窗口上不会触发)。
为存储事件注册处理程序可以通过:
- addEventListener()
- attachEvent() [IE]
- onstorage属性 [FireFox不支持]
与存储事件相关的事件对象有5个重要的属性:(IE8 不支持)
- key:被操作项的键名。调用clear()后,值为null。
- newValue:保存该项的新值。调用removeItem()时,值为null。
- oldValue:改变删除前,保存该项原先的值。插入一个新项时,值为null。
- storageArea:目标window对象上的localStorage或sessionStorage属性。
- url:触发该存储变化脚本所在文档的URL。
localStorage和存储事件都是采用广播机制
的。浏览器会对目前正在访问同样站点的所有窗口发送消息。
2.cookie
- cookie是指web浏览器存储的
少量数据
。 - 最早是设计为被
服务端
所用。 - 最底层看,作为
HTTP协议的一种扩展
实现。 在JS中,cookie用于保存状态以及能够为浏览器提供一种身份识别机制。
但是不会采取任何加密机制。当通过https来传输是安全的,与https协议有关。
检测cookie是否启用:navigatior.cookieEnabled:true [兼容性一般]
有效期和作用域
cookie的有效期
只能持续在浏览器的会话期间
,范围是整个浏览器进程不局限在浏览器的单个窗口
。浏览器关闭后数据就会丢失。
cookie的作用域
是通过文档源
和文档路径
来确定的:默认情况下cookie和创建它的页面有关,并对该页面以及该页面同目录或子目录的其它页面可见。有时希望整个网站都能够使用cookie的值,例如当用户在一个页面表单中填写了邮件地址,为了下次该用户回到这个页面填写表单,或者在网站其它页面的任何地方要求输入账单地址的时候,将其做为默认的邮件地址。要满足这样的需求,可以设置cookie的路径。
同域不同路径path:当路径设置成 “ / ” 等于和 localStorage 有相同的作用域。
同父域不同子域domain:当domain属性设置成父级域名时,可以满足大型网站子域之间互享cookie。
保存cookie
document.cookie = "name=value"
document.cookie = "version=" + encodeURIComponent(document.lastModified);
- cookie属性设置的
值是字符串
- 名值对存储在文档cookie列表中,不允许包含分号,逗号和空白符。因此存储前需要
编码
encodeURIComponent()。读取时使用decodeURIComponent()解码
。 - 以简单名值对存储的cookie数据有效期只在当前浏览器的会话内,浏览器关闭数据就会丢失。想要
延长有效期
,设置max-age属性(单位是秒)。
"name=value; max-age=seconds" 整体是个字符串
//以名值对的形式存储cookie
//同时采用encodeURIComponent()对空格分号等转义
//daysToLive是一个数值,设置max-age属性为该数值表示cookie直到指定的天数
//如果max-age值为0,表示删除cookie
function setcookie(name,value,daysToLive){
var cookie = name + "=" + encodeURIComponent(value);
if(typeof daysToLive === "number")
cookie += ";max-age="+(daysToLive*60*60*24);
document.cookie = cookie;
}
设置cookie的path、domain和secure属性,只需在存储cookie值前,以如下字符串形式追加在cookie值的后面:
;path=path //url:/ 设置cookie的路径
;domain=domain //当前服务器的域及子域 设置cookie的作用域
;secure //表明cookie值的传输方式:默认以不安全的形式HTTP。
改变和删除cookie的值,需要相同的名字,路径和域。
读取cookie
- 读取cookie时,返回的值是一个
字符串
。 - 字符串由一系列名值对组成,名值对间通过“分号和空格”分开,其内容包含了所有作用在当前文档的cookie。但不包含cookie属性。
document.cookie
获取cookie的值。- 一般采用
split()
方法将cookie值中的名值对分离。 - 分离后
解码
,再利用JSON.parse()方法转化成JSON对象
。
cookie的局限性
cookie在不同浏览器上数量和大小都有限制。
浏览器保存cookie不能超过300,为每个服务器保存额cookie不能超过20,cookie保存的数据不能超过4KB。
示例
var cookie = {
/**设置cookie
** name 标识
** value 值
** options {
** 'path': '访问路径',
** 'domain' : '域名',
** 'expire' : 过期时间
}
**/
setCookie : function(name,value,options){
var options = options ? options : {},
path = options.path ? options.path : '/',
domain = options.domain ? options.domain : document.domain,
time = options.expire ? (new Date().getTime() + options.expire * 1000) : '',
expire = new Date(time).toUTCString();
document.cookie = encodeURIComponent(name) + "="+ encodeURIComponent(value) + ";expires=" + expire + ";domain=" + domain + ";path=" + path;
},
//获取cookie
getCookie: function(name){
var arr,
reg=new RegExp("(^| )"+name+"=([^;]*)(;|$)");
if(arr=document.cookie.match(reg)){
console.log(arr);
return unescape(arr[2]);
}
return null;
},
//移除cookie
removeCookie: function(name){
var val = this.getCookie(name);
if(val != null){
this.setCookie(name,val, {expire : - 1})
}
}
}
3.应用程序缓存application cache
应用程序缓存,是从浏览器的缓存中分出来的一块缓存区,要想在这个缓存中保存数据,可以使用一个描述文件(manifest file)
,列出要下载和缓存的资源。
Manifest 文件
manifest 文件是简单的文本文件,它告知浏览器被缓存的内容(以及不缓存的内容)。
manifest 文件可分为三个部分:
- CACHE MANIFEST - 在此标题下列出的文件将在首次下载后进行缓存
- NETWORK - 在此标题下列出的文件需要与服务器的连接,且不会被缓存
- FALLBACK - 在此标题下列出的文件规定当页面无法访问时的回退页面(比如 404 页面)
使用步骤:
1、服务器端需要维护一个manifest清单
2、Html标签用属性manifest引入文件即可
常用API:
核心是applicationCache对象,有个status属性,表示应用缓存的当前状态:
0(UNCACHED) : 无缓存, 即没有与页面相关的应用缓存
1(IDLE) : 闲置,即应用缓存未得到更新
2 (CHECKING) : 检查中,即正在下载描述文件并检查更新
3 (DOWNLOADING) : 下载中,即应用缓存正在下载描述文件中指定的资源
4 (UPDATEREADY) : 更新完成,所有资源都已下载完毕
5 (IDLE) : 废弃,即应用缓存的描述文件已经不存在了,因此页面无法再访问应用缓存
相关的事件:
表示应用缓存状态的改变:
checking : 在浏览器为应用缓存查找更新时触发
error : 在检查更新或下载资源期间发送错误时触发
noupdate : 在检查描述文件发现文件无变化时触发
downloading : 在开始下载应用缓存资源时触发
progress:在文件下载应用缓存的过程中持续不断地下载地触发
updateready : 在页面新的应用缓存下载完毕触发
cached : 在应用缓存完整可用时触发
整个工作流程如下图:
注意事项:
1、浏览器对缓存数据的容量限制可能不太一样(某些浏览器设置的限制是每个站点 5MB)
2、如果manifest文件,或者内部列举的某一个文件不能正常下载,整个更新过程将视为失败,浏览器继续全部使用老的缓存
3、引用manifest的html必须与manifest文件同源,在同一个域下
4、浏览器会自动缓存引用manifest文件的HTML文件,这就导致如果改了HTML内容,也需要更新版本才能做到更新。
5、 manifest文件中CACHE则与NETWORK,FALLBACK的位置顺序没有关系,如果是隐式声明需要在最前面
6、FALLBACK中的资源必须和manifest文件同源
7、 更新完版本后,必须刷新一次才会启动新版本(会出现重刷一次页面的情况),需要添加监听版本事件。
8、站点中的其他页面即使没有设置manifest属性,请求的资源如果在缓存中也从缓存中访问
9、当manifest文件发生改变时,资源请求本身也会触发更新页面资源的请求跟缓存的加载是同时执行的,所以首次更新manifest时,因为页面加载已经开始甚至已经完成,缓存更新尚未完成,浏览器仍然会使用过期的资源;浏览器是当Application Cache有更新时,该次不会使用新资源,第二次才会使用。这个时候update事件中执行window.reload事件。
window.applicationCache.addEventListener(“updateready”, function(){
window.location.reload()
});
10、根据Application Cache的加载机制,如果仅仅修改资源文件的内容(没有修改资源文件的路径或名称),浏览器将直接从本地离线缓存中获取资源文件。所以在每次修改资源文件的同时,需要修改manifest文件,以触发资源文件的重新加载和缓存。这其中,最有效的方式是修改manifest文件内部的版本注释
离线缓存与传统浏览器缓存区别:
离线缓存是针对整个应用,浏览器缓存是单个文件
离线缓存断网了还是可以打开页面,浏览器缓存不行
离线缓存可以主动通知浏览器更新资源
总结:
浏览器的缓存在我们优化页面的时候很有用,特别是在移动端的时候。我们可以用localstorage来存储一些ajax请求的数据,或者一些经常不变的数据,如页面的bar数据等等,可以在结果没有返回的时候先用旧的数据进行填充,避免页面无数据状态过长。同时我们可以利用离线存储的功能来对一些公共的或者常用的js/css文件进行缓存到本地,避免用户网络访问请求,从而节约带宽并且提升网页加载速度。