阅读 81

Cookie存储优化

一、前言

最近几年,前端发展越来越快,已经可以开发“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 也渐渐演化为了一个“存储多面手”——它不仅仅被用于维持状态,他还被迫承担了本地存储的责任,它通常被用于各种场景:

  1. 识别您并进行身份验证。
  2. 要记住一些东西,比如用户点击了哪个商品。
  3. 记录过去的活动,比如用户访问了哪些页面。
  4. 记录用户的信息,比如您的性别,年龄。

三、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:"/"});
        }
    }
复制代码

**  **

  1. 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);
    };
复制代码
  1. 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);
    };
复制代码
  1. 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,以便加速来自此服务器的响应,使此内容接近最终用户。