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 !!! ....
 
 
 
 
 
 
 
 
 
 
 
 
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM