Cookie个数压缩存存储实践

提到cookie,大家都不会陌生的,几乎涉及到交互或统计的WEB系统都会使用到cookie,关于cookie的基础知识网上也有很多,这里推荐两篇文章:

  聂微东的: http://www.cnblogs.com/Darren_code/archive/2011/11/24/Cookie.html#2933759

  张子秋的:http://www.cnblogs.com/zhangziqiu/archive/2009/08/06/cookies-javascript-aspnet.html

cookie,很实用,但是在各大浏览器里面限制比较多,下面是各浏览器对cookie大小和个数的限制:

  IE6.0 IE7.0/IE8.0 Opera FF Safari Chrome
cookie个数  每个域为20个 每个域为50个 每个域为30个 每个域为50个 没有个数限制   每个域为53个
cookie大小 4095个字节 4095个字节 4096个字节 4097个字节 4097个字节 4097个字节

 

 

 

看到这张表,突然感到好忧伤,因为我们的项目还要一直兼容IE6,IE在Cookie方面只能保留20个,较旧的会被清掉。 表中Opera是30个,但是新版的Opera已经开始使用webkit内核了,应该也是50个以上的了。

今天我们来讨论的就是解决cookie个数太多的问题,我的做法就是将多个cookie压缩在一个cookie里面 ,里面涉及到很多问题,如域、路径、有效期等,下来直接看一下源码先:

  1 /**
  2 * cookie操作模块
  3 * @name cookiex
  4 * @function
  5 * @param {String} key
  6 * @param {String} [value]
  7 * @param {{expires:Number|'forever', domain:String, path:String , inCommon: boolean , secure : boolean}} [options]
  8 * @reutrns {String}
  9 * @example
 10 * cookiex(key); //读取cookie
 11 * cookiex(key, null); //删除cookie
 12 * cookiex(key, value, {"expires":forever,"domain":"mydomain.com","path":"/" , inCommon : false }); //设置cookie
 13 */
 14 function cookiex(key,value,options){
 15 
 16         var options = options || {},
 17             ret,
 18             result, 
 19             getRet = function(tkey){
 20                 var tret = '';
 21                 if(result = new RegExp('(?:^|; )' + encodeURIComponent(tkey) + '=([^;]*)').exec(document.cookie)) {
 22                     try {
 23                         tret = decodeURIComponent(result[1]);
 24                     } catch(e) {
 25                         tret = result[1];
 26                     }
 27                 }
 28                 return tret;
 29             };
 30 
 31 
 32         // key and value given, set cookie...
 33         if (arguments.length > 1 && (value === null || typeof value !== "object")) {
 34                 
 35             if (value === null) {
 36                 
 37                 
 38                 //删除forever里面的东西,只改key和expires,在最后才写cookie
 39                 var forever = getRet("my_common_forever"),
 40                     inCommon = false,
 41                     oldKey = key,
 42                     oldDomain = options.domain ,
 43                     oldPath = options.path;
 44 
 45 
 46                 if(forever != ''){
 47                     var foreverList =  forever.split('&');
 48                     for(var i = 0; i < foreverList.length; i++){
 49                         if(foreverList[i].split('=')[0] == key){
 50                             foreverList.splice(i,1);
 51                             key =  "my_common_forever";
 52                             value = foreverList.join("&");
 53                             options.expires = new Date(0xfffffffffff);
 54                             options.domain = "mydomain.com";
 55                             options.path = "/";
 56                             inCommon = true;
 57                             break;
 58                         }
 59                     }            
 60                 }
 61 
 62                 //删除session里面的东西,只改key和expires,在最后才写cookie
 63                 if(!inCommon){
 64                     var session = getRet("my_common_session");
 65                     if(session != ''){
 66                         var sessionList =  session.split('&');
 67                         for(var i = 0; i < sessionList.length; i++){
 68                             if(sessionList[i].split('=')[0] == key){
 69                                 sessionList.splice(i,1);
 70                                 key =  "my_common_session";
 71                                 value = sessionList.join("&");
 72                                 options.expires = null;
 73                                 options.domain = "mydomain.com";
 74                                 options.path = "/";
 75                                 inCommon = true;
 76                                 break;
 77                             }
 78                         }            
 79                     }
 80                 }
 81 
 82                 //如果不在公用cookie里面就直接在最后删除原生cookie就行了,如果在公用cookie里面就改变
 83                 if(!inCommon){
 84                     value = '';
 85                     options.expires = new Date(0);
 86                 }else{
 87                     //删除原生cookie,带直接写cookie操作
 88                     document.cookie = [
 89                         encodeURIComponent(oldKey), 
 90                         '=', 
 91                         "" ,
 92                         '; expires=' + (new Date(0)).toGMTString(), 
 93                         oldPath ? '; path=' + oldPath : '; path=/',
 94                         oldDomain ? '; domain=' + oldDomain : '; domain=mydomain.com',
 95                         options.secure ? '; secure' : ''
 96                     ].join('');
 97                 }
 98             }else{
 99                 //写cookie
100                 if (typeof options.expires === 'number') {
101                     //计时cookie,默认以秒计
102                     var minutes = options.expires, 
103                         t = options.expires = new Date();
104                     t.setDate(t.getSeconds() + minutes);
105                 } else if(typeof options.expires === 'string' && options.expires != 'forever') {
106                     //计时cookie ,根据开发者输入的尾缀定单位
107                     var t = parseInt(options.expires),
108                         suffix = options.expires[options.expires.length -1],
109                         now = new Date();;
110                     if(suffix=="s"){
111                         now.setSeconds(now.getSeconds() + t);
112                         options.expires = now;
113                     } else if(suffix=="m") {
114                         now.setMinutes(now.getMinutes() + t);
115                         options.expires = now;
116                     } else if(suffix=="h") {
117                         now.setHours(now.getHours() + t);
118                         options.expires = now;
119                     } else if(suffix=="d") {
120                         now.setDate(now.getDate() + t)
121                         options.expires = now;
122                     } else if(suffix=="M") {
123                         now.setMonth(now.getMonth() + t);
124                         options.expires = now;
125                     }
126                 } else if(options.expires == 'forever') {
127                     //永久cookie
128                     options.expires = new Date(0xfffffffffff);
129                     if(options.inCommon){
130                         //如果使用公用cookie,强制使用domain : .mydomain.com这个和 path : /
131                         options.domain = "mydomain.com";
132                         options.path = "/";
133                         var forever = cookiex("my_common_forever");        
134                         if(forever!=''){
135 
136                             var retList =  forever.split('&'),
137                                 inForever = false;
138                             
139                             for(var i = 0; i < retList.length; i++){
140                                 if(retList[i].split('=')[0] == key){
141                                     retList[i] = key + '=' + encodeURIComponent(String(value));
142                                     inForever = true;
143                                     break;
144                                 }
145                             }
146 
147                             if(!inForever){
148                                 value = forever + "&" + key + '=' + encodeURIComponent(String(value));    
149                             }else{
150                                 value = retList.join('&');
151                             }
152 
153                         }else{                    
154                             value = key + '=' + encodeURIComponent(String(value));
155                         }        
156                         key = "my_common_forever";
157                     }
158                 } else if( typeof options.expires === "object" && (a instanceof Date)){
159                     //用户自己传入Date对象
160                 } else {
161                     //浏览器进程cookie
162                     options.expires = null;
163                     if(options.inCommon){
164                         //如果使用公用cookie,强制使用domain : .mydomain.com这个和 path : /
165                         options.domain = "mydomain.com";
166                         options.path = "/";
167                         var session = cookiex("my_common_session");    
168                         if(session!=''){
169 
170                             var retList =  session.split('&'),
171                                 inSession = false;
172                             
173                             for(var i = 0; i < retList.length; i++){
174                                 if(retList[i].split('=')[0] == key){
175                                     retList[i] = key + '=' + encodeURIComponent(String(value));
176                                     inSession = true;
177                                     break;
178                                 }
179                             }
180 
181 
182                             if(!inSession){
183                                 value = session + "&" + key + '=' + encodeURIComponent(String(value));
184                             }else{
185                                 value = retList.join('&');
186                             }
187                         }else{                    
188                             value = key + '=' + encodeURIComponent(String(value));
189                         }            
190                         key = "my_common_session";
191                     }
192                 }
193 
194             }
195 
196 
197             //执行操作
198             return (document.cookie = [
199                 encodeURIComponent(key), '=',
200                 options.raw ? String(value) : encodeURIComponent(String(value)),
201                 options.expires ? '; expires=' + options.expires.toGMTString() : '', // use expires attribute, max-age is not supported by IE
202                 options.path ? '; path=' + options.path : '; path=/',
203                 options.domain ? '; domain=' + options.domain : '; domain=mydomain.com',
204                 options.secure ? '; secure' : ''
205             ].join('')); 
206         }
207 
208 
209         // key and possibly options given, get cookie...
210         options = value || {};
211         ret = getRet(key);    
212         
213         //查找永久公用cookie
214         if(ret == '' && key != 'my_common_forever') {
215             ret = getRet("my_common_forever");
216             if(ret != ''){
217                 var retList =  ret.split('&');
218                 for(var i = 0; i < retList.length; i++){
219                     if(retList[i].split('=')[0] == key){
220                         ret = decodeURIComponent(retList[i].split('=')[1]);
221                         return ret ;
222                     }
223                 }            
224             }
225             ret = '';
226         }
227 
228         //查找浏览器进程公用cookie
229         if(ret == '' && key != 'my_common_session') {
230             ret = getRet("my_common_session");
231             if(ret != ''){
232                 var retList =  ret.split('&');
233                 for(var i = 0; i < retList.length; i++){
234                     if(retList[i].split('=')[0] == key){
235                         ret = decodeURIComponent(retList[i].split('=')[1]);
236                         return ret ;
237                     }
238                 }            
239             }
240             ret = '';
241         }
242 
243         return ret;
244 }

一看,操作个cookie两百多行代码,有点小多,这里我来一个个解释一下,为什么会有这么多代码。

 公用cookie :

这里我使用了两个公用的cookie,my_common_session(跟随浏览器进程)和my_common_forever(永久cookie),里面的多个cookie使用 & 符号连接起来,然后用 encodeURIComponent 进行编码。

我把公用cookie的域写死在一个域和路径里面了,这样可以保证读写公用cookie的时候不会因为输入的域不同导致调用不到,一般都是网站的顶级域名,可以按照自己的需求修改。

 cookie添加或修改:

在cookie添加或修改的时候判断inCommon 是否为true,如果是的话,就往对应的公用cookie里面添加,否则便以传统的方式添加或修改cookie。

在添加进公用cookie的时候,需要注意的是公用cookie里面是不是已经存在有了,如果不存在就进行添加,如果之前有过的了,便进行值替换,具体是在代码的第 126~192行间。

添加非公用cookie时跟普通的cookie操作差不多,这里也提供了很多关于时间的参数。

/* 调用案例 */
cookiex("a","value-of-cookie-a"); 
cookiex("a"); //"abcc"

cookiex("b","value-of-cookie-b",{inCommon:true}); 
cookiex("b"); // "bbbb"

cookiex("c","value-of-cookie-c",{expires:"forever",inCommon:true});
cookiex("c"); //"value-of-cookie-c"

cookiex("d","value-of-cookie-d",{expires:"3h"});
cookiex("d"); //"value-of-cookie-d"

 cookie删除:

关于cookie删除操作,这里有两步操作,一步是判断公用cookie里面有没有对应的key,有的话便删除,第二步是删除非公用cookie里面对应的cookie。

/* 调用案例 */

cookiex("d",null) 

可能存在问题及解决:

一个项目里面有不同人对cookie进行操作的时候,可能会同一个key的cookie即存在公用的cookie里面,又存在非公用的cookie里面。在上面的方法里是优先读取了非公用的cookie的,但是这样重复存在难免会造成一些混淆,所以解决方案是团队内部沟通好,约定好一些cookie怎么用。

与服务端结合:

cookie被这样压缩后,服务端就不能直接通过传统的方式去读取这些cookie了,所以我们可以在后台使有类似的方式封装一个方法对cookie进行相应的读写操作。

 

 

posted @ 2014-05-23 11:28  zernmal  阅读(356)  评论(0编辑  收藏  举报