iframe異步加載技術及性能
英文原文:Iframe loading techniques and performance
我們會經常使用iframes來加載第三方的內容、廣告或者插件。使用iframe是因為它可以和主頁面并行加載,不會阻塞主頁面。當然使用iframe也是有利有弊的:Steve Souders在他的blog里面有闡述:Using Iframes Sparingly:
- iframe會阻塞主頁面的onload事件
- 主頁面和iframe共享同一個連接池
阻塞主頁面的onload是這兩個問題中最影響性能的方面。一般都是想讓onload時間越早觸發越好,一方面是用戶體驗過更重要的是google給網站的加載速度的打分:用戶可以用IE和FF中Google工具欄來計時。
那么為了提高頁面性能,怎樣才能不阻塞主頁面的onload事件的來加載iframe呢?
這篇講了四種加載iframe的方法:普通iframe,onload之后加載iframe,setTimeout() iframe和異步加載iframe。每種方法的加載結果我都用IE8的時間線來展示。我建議多注意下動態異步加載這個方法,因為這是性能表現最佳的。另外,還有一種友好iframe(friendly iframe)技術。它可能算不上是iframe加載的技術,但是必須使用iframe,它是無阻塞加載的。
普通方法加載iframe
這是一種人盡皆知的普通加載方法,它沒有瀏覽器的兼容性問題。
<iframe src="/path/to/file" frameborder="0" width="728" height="90" scrolling="auto"></iframe>
使用這種加載方法會在各瀏覽器中有如下表現:
- iframe會在主頁面的onload之前加載
- iframe會在所有iframe的內容都加載完畢之后觸發iframe的onload
- 主頁面的onload會在iframes的onload觸發之后觸發,所以iframe會阻塞主頁面的加載。
- 當iframe在加載的過程中,瀏覽器會標識正在加載東西,處于忙碌狀態。
這里是一個演示頁面,時間線圖顯示出iframe會阻塞主頁面的加載。
我的建議:注意onload阻塞。如果iframe的內容只需要很短的時間來加載和執行,那么也不是個大問題,而且使用這種方法還有個好處是可以和主頁面并行加載。但是如果加載這個iframe需要很長時間,用戶體驗就很差了。你得自己測試一下,然后在 http://www.webpagetest.org/ 也做些測試,根據onload的時間看看是否需要其他加載方法。
在onload之后加載iframe
如果你想在iframe中加載一些內容,但是這些內容對于頁面來說不是那么的重要。或者這些內容不需要馬上展現給用戶的,需要點擊觸發之類的。那么可以考慮在主頁面載入之后加載iframe。
<script> //doesn't block the load event function createIframe() { var i = document.createElement("iframe"); i.src ="path/to/file"; i.scrolling ="auto"; i.frameborder ="0"; i.width ="200px"; i.height ="100px"; document.getElementById("div-that-holds-the-iframe").appendChild(i); }; // Check for browser support of event handling capability if (window.addEventListener) window.addEventListener("load", createIframe, false); else if (window.attachEvent) window.attachEvent("onload", createIframe); else window.onload = createIframe; </script>
這種加載方法也是沒有瀏覽器的兼容性問題的:
- iframe會在主頁面onload之后開始加載
- 主頁面的onload事件觸發與iframe無關,所以iframe不會阻塞加載。
- 當iframe加載的時候,瀏覽器會標識正在加載。
這是是一個測試頁面,時間線圖如下
- 其他等待主頁面onload事件的代碼可以盡早執行
- Google Toolbar計算你頁面加載的時間會大大減少
但是,當iframe加載的時候,還是會看到瀏覽器的忙碌狀態,相對于普通加載方法,用戶看到忙碌狀態的時間更長。還有就是用戶還沒等到頁面完全加載完的時候就已經離開了。有些情況下這是個問題,比如廣告。
setTimeout()來加載iframe
這種方法的目的是不阻塞onload事件。
Steve Souders(又是他?)有一個這種方法的測試頁面(http://stevesouders.com/efws/iframe-onload-nonblocking.php)。他寫道:“src通過setTimeout動態的設置,這種方法可以在所有的瀏覽器中避免阻塞”。
<iframe id="iframe1" src="" width="200" height="100" border="2"> </iframe> <script> function setIframeSrc() { var s ="path/to/file"; var iframe1 = document.getElementById('iframe1'); if (-1 == navigator.userAgent.indexOf("MSIE")) { iframe1.src = s; } else { iframe1.location = s; } } setTimeout(setIframeSrc, 5); </script>
在除了IE8以外的所有瀏覽器中會有如下表現:
- iframe會在主頁面onload之前開始加載
- iframe的onload事件會在iframe的內容都加載完畢之后觸發
- iframe不會阻塞主頁面的onload事件(IE8除外)
- 為什么不會阻塞主頁面的onload呢(IE8除外)?因為setTimeout()
- 當iframe加載的時候,瀏覽器會顯示忙碌狀態。
下面是時間線圖:
因為IE8的問題,這種技術就不適合很多網站了。如果有超過10%的用戶使用IE8, 十分之一的用戶體驗就會差。你會說那也只是比普通加載差一點點,其實普通加載性能上也不差。onload事件對于10%的用戶來說都更長。。。。額,你自己考慮吧。但是最好在看了下面這個很贊的異步加載方法之后再決定吧。
我在參加Velocity 2010的時候,Meebo的兩個工程師(@marcuswestin and Martin Hunt)做了一個關于他們的Meebo Bar的演講。他們使用iframe來加載一些插件,并且真正做到了無阻塞加載。對于有的開發者來說,他們的做法還比較新鮮。很贊,超級贊。但是一些原因導致這種技術沒有得到相應的關注,我希望這篇blog能把它發揚光大。
<script> (function(d) { var iframe = d.body.appendChild(d.createElement('iframe')), doc = iframe.contentWindow.document; // style the iframe with some CSS iframe.style.cssText ="position:absolute;width:200px;height:100px;left:0px;"; doc.open().write('<body onload="'+ 'var d = document;d.getElementsByTagName(\'head\')[0].'+ 'appendChild(d.createElement(\'script\')).src'+'=\'\/path\/to\/file\'">'); doc.close(); //iframe onload event happens })(document); </script>
神奇的地方就在<body onload="">:這個iframe一開始沒有內容,所以onload會立即觸發。然后你創建一個script元素,用他來加載內容、廣告、插件什么的,然后再把這個script添加到HEAD中去,這樣iframe內容的加載就不會阻塞主頁面的onload!你應該看看他在個瀏覽器中的表現:
- iframe會在主頁面onload之前開始加載
- iframe的onload會立即觸發,因為iframe的內容一開始為空。
- 主頁面的onload不會被阻塞
- 為什么這個iframe不會阻塞主頁面的onload?因為<body onload="">
- 如果你不在iframe使用onload監聽,那么iframe的加載就會阻塞主頁面的onload。
- 當iframe加載的時候,瀏覽器終于不顯示忙碌狀態了(非常好)
我的測試頁給出下面的時間線:
轉義字符讓代碼看著有些難受,這都不是問題。試試吧。
友好型iframe加載
這是用來加載廣告的。雖然這不是一種iframe的加載技術,但是是用iframe來盛放廣告的。他的亮點不在于iframe如何加載,而是主頁面、iframe、廣告如何協同工作的。如下:
- 先創建一個iframe,設置他的src為一個相同域名下的靜態html文件。
- 在這個iframe里面,設置js變量inDapIF=true來告訴廣告它已經加載在這個iframe里面了。
- 在這個iframe里面,創建一個script元素加上廣告的url作為src,然后像普通廣告代碼一樣加載。
- 當廣告加載完成,重置iframe大小來適應廣告。
- 這種方法也沒有瀏覽器的兼容性問題。
Ad Ops Council在推薦過這個方法,AOL也是用這種方法。想看看源碼:這里有一個。一家瑞典的出版社Aftonbladet對于這種加載有很不錯的結論:在他們的主頁上,加載時間減少30%,用戶每周增加7%,新聞部分的點擊量增加35%。我建議可以看看他們的資料:High Performance Web Sites, With Ads: Don’t let third parties make you slow
我沒有創建相關的測試頁,所以也沒有第一首的資料。從我調研的結果來說:
如果你只想在你的網頁上調用一個確定的src地址的iframe的話這個方法不是很有用。
如果你想在網頁上展示多個廣告,比較靈活的方法的是:加載一個廣告,然后更新iframe加載另一個主頁面的DOMContentLoaded時間不會被阻塞,頁面渲染也不會被阻塞,當然,主頁面的onload事件還是會被阻塞。