IE6 与 GZIP, BUG汇总

bug1描述:
IE6部分版本,某些情况下,开启gzip的资源,会不渲染或不执行(如果是.js的话.)
 
会引发此bug的条件:
1. 首先,必须由a页跳转到b页面 : 即 a页面有 location.href = b页面.(点链接,form post,replace, assign等方式都会导致问题,包括target=_blank弹窗的情况)
2. b页面自身,或使用动态创建脚本(硬编码script src=xxx 也存在此问题) 的响应头中包含下面情况:
    cache-control 包含下列伪指令:
        (1) no-store
        (2) no-cache + 其他与缓存新鲜度检验有关头共存时, 如 max-age=xxx (xxx无所谓.0 或3000都会触发,) 或 no-cache + must-revalidate 甚至是,no-cache, pre-check=0等情况..
        (3) no-cache独立存在时,体现为一种不稳定情况.可能会触发.但也不是100%.仅仅是偶尔...
 
   ps: 本bug ,与 http1.0 头域 : Pragma : no-cache ,无关. 也于是否chunked输出无关.
 
  注意: 不配置cache-control并不写expires 同样会触发.
 
 
 
更神奇的是,一但,开启gzip, 则 i6会无视ETag .(ETAG,bug没有被我独立列出来.因为它的严重性并不太高..) 不会尝试2次握手.完全放弃使用本地缓存....也就是说,ie6发起请求时,不会带有 if-none-match头...
Last-Modified则无此问题. 
但是要注意的是: IE6会开启gzip的情况下, 在if-Modified-Since 值中加入length = xxxx .字样.  我不确定所有的web server 在检测该值是 检验相等性,还是检测包含性.尤其是大家用nodejs写一个轻量级web server的时候.一定要注意这个问题
.务必使用 headers['if-Modified-Since']indexOf(缓存日期) > -1 的逻辑来做.
 
如图:
 

 
IE7+ 则无此问题..
 
解决办法:
1. 放弃gzip.
2. 放弃cache-control 中的 no-cache,no-store头域. 比如 单独使用max-age=0.并对不支持http1.1的老浏览器配合Expires = 一个过期时间.
 
 
ps :
1.与bug1类似的情况 还有ajax , 或动态发起的jsonp 等请求. 当某个页面有这种情景,且被请求资源开启gzip , cache-control有前面提到的那些配置.那么同一个资源(即使querystring有不同),第二次对其发起请求,就可能导致ajax readyState==4的情况不被出发,或jsonp的脚本不被执行. 解决办法同上.
 
2.类似的,如果多个iframe 自身就有gzip,则也会引发问题.即不渲染,甚至可能会定位到404(抓包一切正常).
 
 

 
 

msdn上的说法:

 
To work around this problem, you can do either of the following:

If you use a Cache-Control: no-cache HTTP header to prevent the files from caching, remove that header. In some situations, if you substitute an Expires HTTP header, you do not trigger the problem.

-or-

Do not enable HTTP compression for the script files.
 
      显然,微软给了我们两条路,去掉no-cache头,或者使用Expires指定过期时间,强制使其过期代替no-cache, 或者别压缩. 而pre-check=0.显然可以代替Expires做到这件事.

一个常见的误区是,有些朋友收借助 cache-control ,IE定义的扩展指令来解决这个问题.参考下面的描述:
 

扩展指令 中一个常见的东西是 none-check post-check 和 pre-check. 这玩意是IE5被加入的. 所以如果响应头中有这几个扩展指令,那么IE就会认得他们, 我经常在一些 为了解决 cache + gzip 命中ie6 JSONP 请求,导致脚本不执行bug的方案中见到这几个扩展指令,其目的是为了让IE放弃使用本地缓存.  我倒是觉得,对IE6放弃使用gzip,是更合理的做法.  当然缺点也很明显, 如果是cdn部署静态资源.显然这样做会很困难. 

   关于这几个扩展指令的, 参考msdn 的描述:http://msdn.microsoft.com/en-us/library/ms533020%28VS.85%29.aspx#Use_Cache-Control_Extensions

  • post-check
    • Defines an interval in seconds after which an entity must be checked for freshness. The check may happen after the user is shown the resource but ensures that on the next roundtrip the cached copy will be up-to-date.
  • pre-check
    • Defines an interval in seconds after which an entity must be checked for freshness prior to showing the user the resource.

    一张图:

           

         简单来说, 就是控制IE,如何使用本地缓存 ,如果缓存时间,超过post-check的值,就要保证下一次请求该资源,去要验证过的新鲜的.而pre-check则是超过了,就马上给个新的. no-check就无需解释了..
        但这个方法本质上是无法绕过这个bug的... 所以根本原因还是 ,去掉no-cache,no-store. 或放弃压缩.
 
 

 
bug2 描述:
 
 
当一个页面,有多个iframe , 而每个iframe 都引入 a.js 时, 如果a.js 开启gzip ,且无论是否有缓存头,比如 Expire= xxx, Control-Cache: max-age= xxxx 的情况下
那么这个a.js就可能在某次不被执行. (请注意这个问题和 之前gzip第一个bug情形不同.上一个是no-cache出现bug. 而这个,已经和http,cache无直接关系了.当然,如果你配置了no-cache,那么这个bug也是肯定会触发的,具体原因,请看解决方案中的第三条.所以某种角度来说,这个问题,可能和第一个bug描述,仍然有间接关系.只不过,我个人测试情况是,20个iframe,如果请求的js都是不同的url,那么即使配置no-cache,也没有触发我们的bug2,但是这回带来bug3.我们会在bu3中描述它. 但如果配置no-cache,多个iframe中请求相同的url的js,那么bug2就仍然会被触发.这是因为我们预加载已经失去意义.
 
分析: 大概是高并发请求时,IE共享以某个url为标签的资源的状态时,在某种特定情况下,把n个独立请求的相同资源的状态给共享了,比如把某脚本的待执行状态标为已执行过. 但是为什么,只有gzip的资源才会发生.就不得而知(更重要的是,并不直接与资源是否具备缓存能力有直接关系.). 一切都是黑盒推测.所以也做不得准...
 
ps: iframe 下引入多个脚本,是可以并行加载,且无视http连接数限制的
 


如果a.js在 iframe中是document.write,或appendChild等动态方式.那么bug发生几率会都陡升.

如果a.js在同一个页面被引入多次,则不会有任何问题. 原因是,IE6+有一个优化策略.当同一个页面多次引入一个相同的资源时,后续资源不会走网络模块,也就是说,不会发起请求.而直接从以资源url为索引的内存中直接使用该资源.  所以就不会发生问题.

IE7+,在前面多个iframe中引入 同一个.js时,会有类似 同一页面多次引入同一.js时同样的优化策略.不发起请求. 不考虑IE7是否有gzip bug. 即使有,也会因为,没有走网络模块而绕过了gzip bug.


解决方案 :
1. 放弃缓存,比如去掉max-age= xxx,去掉Expires ,或让他们为一个过期值. 或添加 no-cache,no-store.(此项是不被推荐的,因为它只是降低bug触发几率)
2. 放弃gzip .
3. 让IE6,在top页预加载一次.js资源. 这样, 在多个iframe中. 引入的.js,会直接走cache.在后续iframe请求.js 都走cache的情况下会绕过这个bug. (如果用户是ctrl+ f5,强制刷新.都不走cache,当iframe很多,资源加载很多次时bug触发概率也会陡升,也就是说,预加载资源只能解决常规问题.一些特殊情况仍然无法绕过. 当然,普通的f5是ok的因为普通的f5,会触发IE6的优化策略.这时候其行为会像前面提到的IE7+那样,对多个相同的资源,只走一次网络模块.所以就绕过了bug,而ctrl+f5则有几个就走几次网络模块.. 更重要的是,如果想让预加载产生效果,我们就必须给资源开启缓存. 如果资源不能被缓存,则预加载就失去了意义.这是要注意的情况.)
 
我个人建议采用第三种. 在top页预加载一次.

为了不浪费客户端性能, 我建议使用下面的代码来进行预加载.

  
<!--[if IE 6.0]>
<script src="xxxx.a.js" type="text/c"></script>
<![endif]-->
 

这个代码的作用是,只有IE6进行预加载,而且 借助type="text/c" 让IE6只加载a.js 而不执行它....
 
 
 
 
 
bug3 描述
 
如果你的浏览器是IE6 SP2-(SP3已经修正这个问题,此问题与iframe与否,无直接关系,BUG1,BUG2,的场景,都可能触发BUG3).当你引入多个不同url的xxx.js时, 第一次后, f5刷新浏览器.那么ie就会忘记你的js的编码. 比如我们在xxx.js中输出了一些汉字.而我们引入该js,编码要么是GBxxxx或GBk,要么是utf - xx. 那么就会出现乱码,甚至是因为乱码导致js解析失败.抛出异常. 而首次访问,或ctrl+f5强制刷新,则无此问题...   
 
解决办法:
 
1. 不要有no-cahce,no-store,等等.
2. 放弃gzip
3. ...... 好吧,真的没有3了..
 
 

总结:
 
这真是一个异常艰难的总结,但我们总要面对这些让人吐血的问题,不是么?
 
bug触发的条件,环境很复杂,绝大多数情况下,我们可以认为是某个资源多次被加载则触发的可能性会陡升
 
假设, 你是用jsonp,等方式加载数据.又需要开启gzip ,而该数据总是动态的,(即我们不希望它被任何中间环节缓存).而且,你又要保证这玩意,不会在多个iframe中存在.而仅仅服务于某一个特定页面的话, 那就记得别用Cache-Control:no-cache,no-store ,而改用Expires = 过期时间 + max-age=0 来搞定.
 
假设我们期待某些资源总是被缓存的. 又不得不开启gzip ,而资源,又总是出现在一些iframe中, 那么记得.在top页, 预加载他们是个不错的选择.
 
否则,对ie6 放弃 gzip 吧,兄弟们. gzip对于ie6来说,真是万恶之源啊...(nginx 针对ie6,不开启gzip,很简单,只要一行短短的配置指令即可.如果需要,请查文档.nginx真的很贴心.可以方便的针对某一种ua,开启或关闭某些东东.)
 
最后不得不提的是.我之前一再说过一个词,就是不渲染. 那么解释下,不渲染是指,如果资源是一个html. 那么这个html里的任何东西,都不会渲染. 内部资源也不会加载. IE6,SP2-遇到编码问题,也是一样的....
 
 
好吧.  最后 IE6 SUCKS !!! ....
 
 
 
 
 
 
 
 
 
 
 
 
 
 
posted @ 2012-04-28 15:28 Franky 阅读(...) 评论(...) 编辑 收藏