一、前言
最近几年,前端发展越来越快,已经可以开发“WebApp”——它即开即用,用完即走。一个优秀的 WebApp 甚至可以拥有和原生 App 媲美的功能和体验。我认为,WebApp 就是我们前端性能优化的产物,是我们前端工程师对体验不懈追求的结果,WebApp 优异的性能表现,要归功于浏览器存储技术的广泛应用,但是还有很多旧且庞大的项目,他们还用着web早期的存储策略,就是用cookie来做存储,所以我们来讲一讲cookie的存储优化,这是整个前端方向性能优化方向的一小部分。前端发展越来越快速,前端的逻辑交互也更加的复杂,不做优化那么用户体验会变得很差,从而会引发一系列的问题,前端性能优化不可懈怠。
1.前端性能优化最重要的方向有两个:
- 网络层面。
- 渲染层面。
2.网络层面的优化又分为 :
- 请求过程的优化。
- 减少网络请求。
cookie存储优化就属于减少网络请求的这个方向。

二、什么是cookie?cookie在web早期被用来做什么?
cookie是从web服务器发送并存储到您浏览器的文本文件,这个文本文件的内容是干嘛用的?
在 Web 开发的早期,人们亟需解决的一个问题就是状态管理的问题:HTTP 协议是一个无状态协议,服务器接收客户端的请求,返回一个响应,故事到此就结束了,服务器并没有记录下关于客户端的任何信息。那么你再一次发送相同的请求的时候,服务器不知道你还是上次那个,那么就会再次去取数据返回,非常不必要。在这样的背景下,cookie应运而生,cookie被用于包含身份和浏览器信息,作为一种来解决http是无状态协议的事实。
一张图告诉你cookie如何起作用的:

cookie的本职工作是“维持状态”,但在web开发的早期, 随着前端应用复杂度的提高,Cookie 也渐渐演化为了一个“存储多面手”——它不仅仅被用于维持状态,他还被迫承担了本地存储的责任,它通常被用于各种场景:
- 识别您并进行身份验证。
- 要记住一些东西,比如用户点击了哪个商品。
- 记录过去的活动,比如用户访问了哪些页面。
- 记录用户的信息,比如您的性别,年龄。
三、cookie作用这么大,那为什么需要对cookie存储做优化呢? **
1.cookie的性能劣势
- cookie它不够大,cookie最大只有4KB,cookie被赋予的作用就只是用来“维持状态”,而不是存储数据的,如果超过了4KB,那么存储的数据信息就会被截断,结果可想而知。
- cookie是负责“维持状态的”,在每一次( 同域 )所有请求的http请求,cookie都会在浏览器和服务器之间“飞来飞去”,不论你cookie存储的数据需不需要被发送到服务器。
2.cookie的局限性
从上面两点可以看来,cookie只能用来存储少量的信息(大小只有4kB)、过量的cookie会产生巨大的性能浪费,项目会发送的网络请求是很庞大的。试想此刻我们的请求仅仅是一张图片,我们也要携带一个cookie发送请求,cookie里存储的信息现在我也并不需要,可它一定会跟着走,有种拖泥带水的感觉了,cookie虽然小,但请求可以有很多,随着请求的叠加,积少成多,这样不必要的cookie带来的性能开销是你无法去想象的。
3.下面我分享cookie一种优化的实现,这个的理论依据是:
- 优化cookie网络传输中cookie占比 。
- 优化cookie浏览器 cookie中的数量。
下面代码提供了三种不同的存储方式:
* 存储优先级:优先选择存储在HTML5本地存储(localStorage),其次选择cookie存储
*
* 在操作添加浏览器存储的时候,需要在dic字典中添加对应的索引,建议从按顺序添加
* */
;(function($, FUNC, undefind){
//是否有本地存储
var hasLocalStorage = !!window.localStorage;
/*
* dic 存储字典
* --| bit 二进制位存储字典
* --| tens 十进制“位数”存储,个十百千 分别代表0-9不同的状态
* --| array 数组存储,其实也是字符串纯粹,将不同状态存进数组中,根据特定的下标进行存取
* aa bb cc dd ff gg范例
* */
var dic = {
"bit":{
//[对外key]: [内部存储二进制对应的位]
//"aa":0,
},
"tens":{
//[对外key]: [内部存储十进制对应的位]
//bb:0,
//cc:10
},
"array":{
//[对外key]: [内部存储数组对应的下标]
//"ff":0,
//"gg":1
}
};
//存取函数,优先存储在localStorage,其次存在cookie
//top.localStorage是为了尽量获取顶级window的本地存储
function setItem(key, val){
if(hasLocalStorage){
FUNC.getTopWindow().localStorage.setItem(key, val);
}else{
$.cookie(key, val, {expires:1800,path:"/"});
}
//获取浏览器中存储的值
function getItem(key){
if(hasLocalStorage){
return FUNC.getTopWindow().localStorage.getItem(key);
}else{
return $.cookie(key, {path:"/"});
}
}
** **
- Func.setBitMemory
Bit二进制存储 ——> 存储boolean值
//以二进制位进行bool存储。设置存储bool值的位存存储,key应在dic中先定义对应的位
FUNC.setBitMemory = function(key, val) {
//从字典中查询对应的存储位
var bit = dic.bit[key];
// 保护
if (typeof bit != "number"){
throw new Error("参数错误!!未配置dic字典");
return;
}
// 0-30,31-61,61-91 这种规律进行分组,存到JBM0,JBM1,JBM2中
var flagIndex = parseInt(bit/31),
bitSeat = bit%31, // 计算出真正需要进行位移的数字
item = getItem("JBM" + flagIndex);
if (item == null){
item = 0;
} else {
item = parseInt(item);
}
if (Boolean(val)){
item |= (1 << bitSeat);
} else {
item &= ~(1 << bitSeat);
}
setItem("JBM" + flagIndex, item);
};
- Func.setTensMenory
Tens十进制位数存储 ——> 存 0-9这十种状态
//以十进制字符串存储,每一位都为【0-9】的其中一种状态值, key应在dic中先定义对应的位
FUNC.setTensMemory = function(key, statusNum){
var tens = dic.tens[key];
if(typeof tens != "number" || statusNum > 9 || statusNum < 0){
throw new Error("参数错误!!");
return;
}
//seat 为应存在该val的第几位
var seat = parseInt(tens),
item = getItem("JTM") || "",
zeroAdd = 0;
//获取数据 空则新加所需要位数的0
//如果不为空的时候,添加所需要位数的“000”字符串
if(item == ""){
zeroAdd = seat;
}else{
zeroAdd = seat - item.length;
}
for(var i = zeroAdd; i > 0; i--){
item += "0";
}
//对应位置进行赋值,
item = item.split("");
item[seat] = statusNum;
item = item.join("");
setItem("JTM", item);
};
- Func.setArrayMemory
数组存储Array数组存储 ——> 存字符串
//以Array.join(",")字符串形式存储,数组下标为字典中key的val, key应在dic中先定义对应的位
FUNC.setArrayMemory = function(key, val){
//从字典中查找对应的位置
var seat = dic.array[key];
if(typeof seat != "number"){
throw new Error("参数错误!!");
return;
}
//获取数组ID,也就是该数据需要存在第几个数组里
var itemsString = unescape(getItem("JAM") || ""),
itemsArray = itemsString.split(",") || [];
itemsArray[seat] = val;
itemsString = itemsArray.join(",");
setItem("JAM", escape(itemsString));
};
通过这种方式,来将对应特征的数据来进行序列化存储,从而达到降低数据存储大小。
四、优化方案
1.最小化cookie的大小
在cookie中存储过多数据会对性能产生不利影响。最大限度地减少cookie中存储的数据量。保持在1kb或更低。如果您发现自己在cookie中存储了大量信息,请改为使用其他机制:
采用HTML5的Web Storage 维护本地应用程序的状态
维护唯一的会话ID并将用户状态存储在服务器上(使用由中间件逻辑控制的临时存储,即数据库或文件中的会话表)。
2.考虑使用web storage存储信息
Web Storage 是 HTML5 专门为浏览器存储而提供的数据存储机制。它又分为 Local Storage 与 Session Storage。
3.Web Storage存储信息的优势
- 存储容量大:Web Storage 根据浏览器的不同,存储容量可达到 5-10M 之间。
- 仅位于浏览器端,不与服务端发生通信。
- web Storage的支持度

4.设置无cookie子域获取静态资源
如果您的cookie大小开始超过1kb,那么设置无cookie域将对性能产生积极影响,尤其是当您的页面包含多个文件请求时。
- 设置 resource.yourdomain.com子域,并使用它来访问公共文件。这将成为您的无cookie域。
- 将静态资源移动到此域。示例包括:页眉,页脚,常见样式表,常见JS,包括库,图像等。
- 根据所有其他性能最佳实践进行设置:即gzip,keepalive,minification,缓存,SSL调优,TCP CWND调整等。
- 理想情况下,在此前面放置CDN,以便加速来自此服务器的响应,使此内容接近最终用户。