[轉]預加載資源研究

原文:

http://www.otakustay.com/prefetch-resource/

什麼是預加載javascript

所謂預加載,就是經過必定的編程方法,使瀏覽器在空間的時候,在後臺經過HTTP請求訪問某些資源。當用戶在一段時間後真正使用這些資源的時候,相比一個完整的(返回200)的請求,能夠更快地得到這些資源(返回304或者直接命中瀏覽器緩存)。css

預加載在部分狀況下有着十分重要的意義,特別是當肯定某些資源用戶在短期內會使用,如分頁列表的上一頁和下一頁、以及一些經常使用的LOGO之類的圖片等。html

預加載資源可能的方式

預加載的原理就是想辦法發送一個HTTP請求,對響應的緩存等都由瀏覽器完成,所以一切有可能讀取遠程資源的方案均可以成爲預加載資源的方案,大體有如下幾類:java

常規方式

  • 使用script標籤:<script type="other/prefetch" src="some.res"></script>
  • 使用img標籤:<img src="some.res" />
  • 使用iframe標籤:<iframe src="some.res"></iframe>
  • 使用XMLHttpRequest加載:$.get('some.res');
  • 使用Flash進行加載:須要編寫特定的Flash

很是規方式

  • 使用背景圖片:<div style="background-image: url(some.res);"></div>
  • 使用object或embed標籤:<object type="other/prefetch" src="some.res"></object>
  • 使用link標籤並修改media:<link rel="stylesheet" href="some.res" media="prefetch" />
  • 在CSS中作import:@import "some.res"

新一代方法

  • 使用Link Prefetch:<link rel="prefetch" href="some.res" />
  • 使用WebWorker:var worker = new Worker('some.res');
  • 使用@font-face:@font-face { font-family: prefetch; src: url(some.res); }

方法有不少,也可能會有更多,具體的使用方式就不詳細說明了,具體須要注意的細節會在後文詳細描述。編程

預加載資源方式的評估指標

每一種方式或多或少都有其長處和缺點,本次主要按如下幾個維度進行評估:跨域

[A]瀏覽器兼容性
主要考察包括IE6-八、Firefox3.5+、Chrome7+、Opera9+以及Safari4+的兼容性。
[B]資源位置的覆蓋性
主要考察是否有跨域政策的限制,是否能讀取第三方的資源。
[C]引入第三方資源的安全性
主要考察是否會對加載的資源進行解析和執行,是否可能產生如XSS等安全問題。
[D]引入資源類型的覆蓋性
主要考察是否能夠引入不一樣類型的資源,包括text、image、script、html等。
[E]是否能夠肯定什麼時候完成預加載
因爲預加載資源用時的不肯定性,有可能致使用戶在資源未加載完成時產生行爲致使加載請求被中斷。所以須要考察資源的加載完成是否可控,主要考察是否有load、error、readystatechange等事件。
[F]積累的標籤的清理可行性
若是預加載資源的方法會引入多餘的標籤,如link、script等,須要考察在資源加載過程當中,將對應的標籤刪除是否會致使請求中斷。

評估中出現的問題

隨着不斷深刻,各類方案的缺陷也被一點點挖掘,如下是一些不太容易注意到的奇怪的問題。瀏覽器

script標籤

在Firefox下,當script標籤的type屬性是Firefox沒法識別的腳本類型時,Firefox不會發送任何請求,基本上除了type="text/javascript"之外,Firefox都不予理睬。緩存

img標籤以及background-image

在Firefox下,當img標籤或者background-image樣式請求的內容返回的Content-Type不是image大類時,其響應體(Response Body)只會被接收1個包,其後的內容所有丟棄。安全

Flash

對於跨域的請求,Flash會先讀取對方服務器上的策略xml文件,如策略文件容許跨域,則會進行加載。服務器

object或embed標籤

在Firefox下,不管元素是否隱藏,都會提示要求安裝插件,固然這插件是找不到的。

在Chrome下,若是元素被隱藏,則不發起請求;元素未隱藏則提示安裝插件。

在下載完插件前,全部瀏覽器都不會發起請求。

但若是object標籤的type使用text/plain則不存在安裝插件的問題,不過悲劇的是在IE下不會發出請求,而在Firefox和Chrome下會解析執行HTML資源

須要注意的是,object元素有其特殊性,建立一個object元素的代價遠遠大於其餘元素。

link標籤

在IE下有load事件,Opera中能夠定時查看readystate以肯定是否完成了請求,在其餘瀏覽器中則不存在。

Link Prefetch

現階段僅Firefox給予了支持,該功能在HTML5草案中,很是值得期待。

WebWorker

使用WebWorker加載的腳本文件會馬上被解析和執行,雖然在worker中的global對象是帶有必定限制的,但依舊沒法徹底阻止第三方腳本注入有害的代碼。

@font-face

@font-face僅在樣式表中定義是不會發起請求的,必須建立一個元素,將其font-family設爲該font-face,而且該元素必須被添加到DOM樹中才會產生請求。

評估表格及基本判斷

表格從前文所述的A-F共6個方面來考察各類預加載資源的方式,以期較爲直觀地去評價各類方式的優劣。

[A]瀏覽器兼容性    [B]是否跨域    [C]安全性    [D]資源類型    [E]是否有onload    [F]是否能夠刪除標籤
*:只實測了DOM元素被刪除後的效果,因沒法控制GC,未測元素對象被GC的狀況。
  [A] [B] [C] [D] [E] [F]*
script 1 1 1 0 1 1
img 0 1 1 0 1 1
iframe 1 1 0 1 1 1
XMLHttpRequest 1 0 1 1 1 1
Flash 1 0 1 1 1 1
背景圖片 0 1 1 0 0 1
object/embed 0 1 0 0 1 1
link + media 1 1 1 1 0 1
@import 1 1 1 1 0 1
Link Prefetch 0 1 1 1 0 1
WebWorker 0 1 0 1 0 1
@font-face 0 1 1 1 0 1

從表格展示的數據來看,link+media的方式以及css @import方式都比較優秀,惟一的遺憾是沒法得到其是否加載完成,所以須要站點自身經過研究用戶的行爲,保證用戶2次操做的間隔足夠完成資源的加載。

其餘考慮

  • Link Prefetch做爲HTML5草案中的標準,且瀏覽器底層級別支持,所以瀏覽器能夠在帶寬空餘地時候才進行預加載,用戶也能夠經過必定的方式關閉該功能,各方面都有不俗的表現,若是能夠推廣到主流瀏覽器,應該是做爲最值得推薦的方案。
  • @import須要服務器端輔助,將須要加載的資源打包成一個css格式的文件,文件中包含若干個@import聲明,可是不管如何,瀏覽器會須要多一個請求用於加載這個動態生成的css文件。
  • link+media的方式中,能夠經過修改link的href屬性,使用一個link加載多個資源,不會由於標籤過多致使DOM結構的臃腫以致於影響性能。
  • 在IE下,使用link+media的方式,不管下載來的內容是否被解析,IE會對頁面進行一次redraw,這一次redraw的性能損失很是小,大體只是進入了redraw方法,但並無真正地從新進行佈局。
  • object標籤必須與DOM樹相連纔會加載資源,使用object標籤時,在非IE瀏覽器下,能夠將object標籤的寬和高均設爲0,但IE不行。在IE中能夠將寬高均設爲1,並使用visibility: hidden; position: absolute; top: 0; z-index: -1000;的樣式來將其隱藏。固然既然IE不會發起請求,就怎麼設也沒意義了……

結論?

根據使用場景的不一樣,不會有一種萬能的最佳解決方案,但大體能夠總結以下:

  • 加載同域資源,使用XMLHttpRequest便可。
  • 加載第三方可信任的資源,如同公司內不一樣系統,可使用iframe加載非HTML資源。
  • 加載圖片資源,使用img是最好的方式,但須要注意在請求過程當中img不能被GC回收
  • 對於第三方的不可信任型資源,考慮使用link+media的方式加載,但沒法肯定加載完成的時間點。
  • 若是能夠判斷瀏覽器,針對Firefox使用Link Prefetch,針對IE使用img標籤,其餘瀏覽器使用script標籤算是一種較爲完美的解決方案。
  • 從將來看,Link Prefetch必將是大勢所趨,你們給WHATWG提意見,讓他加上onload吧。

參考資料

相關文章
相關標籤/搜索