長時間以來阿裡媽媽的廣告載入策略一直存在些問題,很多頁面也因為阻塞式的廣告載入而被拉低性能,影響用戶體驗。畢竟阿裡媽媽廣告的渲染依賴於諸多嵌套的document.write。ControlJS的目標就是解決js的阻塞式渲染,因此靈玉急不可耐想與同仁們共同去揭秘ControlJS……
Steve Souders在2010年12月份發布了ControlJS項目,該項目是讓開發者更好的去控制javascript文件的下載和執行,從而提高了頁面腳本的加載速度。
Steve提出了一個非常具有創造性的思想,就是預先異步下載javascript文件而不解析執行,直到需要的javascript處理時才去真正的執行。這一點得到了很多人的關注與驗證。Nicholas Zaka也因ControlJS引發了很多思考,並分析了ControlJS和LABjs 的區別所在,詳細內容可以閱讀Thoughts on script loaders和Separating JavaScript download and execution。Steve使用3篇博聞詳細介紹了ControlJS:異步加載、延遲執行、重寫document.write。
ControlJS的原理
異步加載
ControlJS本身是異步進行加載的,首先將script的標簽type屬性值更改為浏覽器無法識別的類型,這樣浏覽器不會認為這是一個腳本。本身異步加載的ControlJS執行時開始遍歷type=”text/cjs”的script標簽(包括內嵌腳本),如果存在”DATA-CJSSRC”屬性將創建IMAGE或者OBJECT對象(依賴浏覽器而選擇),去異步預下載腳本文件並緩存文件,直到window.onload時解析並執行javascript,同時第二次去遍歷遺漏的script標簽。具體操作可看Async WITH ControlJS
延遲執行
浏覽器在解析執行javascript階段是阻塞任何操作的,這時的浏覽器處於假死狀態,Steve分析了美國的Alexa前10名網站的腳本初始需要加載執行情況,發現只有29%是需要頁面加載時初始化解析執行的,而其他71%的腳本是在觸發交互時才會執行,壓縮後這些腳平均加載是229 kB,未壓縮是500KB,這是個大量的數據。
我們希望的結果是在頁面加載中不去解析執行javascript,只是提前預下載好文件。例如通用的點擊彈出二級導航,用戶有可能永遠都沒有點擊導航的行為,此時導航的功能腳本根本毫無用處。但是人們在點擊導航時不希望等太久javascript的執行,所以ControlJS會提前下載文件,這樣javascript只是解析執行,不會花時間放到下載文件上。代碼一目了然,具體操作可看Menu WITH ControlJS
重寫document.write
在默認情況下這些異步腳本都是在window.onload解析執行,如果此時腳本調用window.write方法,將導致一個不希望發生的問題,就是整個頁面被window.write的輸出內容替換,所有頁面內容將被刪除,ie下將處於停滯狀態。產生問題的原因是由於在docuemnt被加載完後調用docuemnt.write方法時將會自動去觸發document.open,寫入任何處於打開狀態的doucment都將會會替換整個頁面的內容。這便導致目前為止所有異步腳本無法延遲document.write的問題,ControlJS的處理方法是重寫docuemnt.write,如下:
ControlJS創建一個dom元素(span),將其插入當前被解析執行的script標簽之前,並且設置SPAN的innerHTML的值為docuemnt.write的內容。具體操作可看document.write WITH ControlJS
用ControlJS優化阿裡媽媽廣告
對於現在大部分的廣告形式都是采用document.write方式寫入,無法將這些內容異步處理是開發者非常頭疼的問題。ControlJS可以為我們解決這類煩惱。
沒有應用ControlJS的網絡圖。DEMO可以看http://chesihui.github.com/ad-demo.html
應用ControlJS優化後的網絡圖。DEMO可以看http://chesihui.github.com/ControlJS-demo.html
ControlJS的局限性
ControlJS存在一個問題是在document.write中多層嵌套script標簽時,頁面仍然存在觸發document.open的問題。查看源代碼發現在執行完一個javascript後都會恢復document.write的原生方法:
動態腳本的異步加載,同樣使得document.write方法也是異步執行,因此不能恢復document.write的原生功能。復現的情況如 DEMO 。注釋這段腳本雖然解決了不觸發window.open的問題,但是同樣的異步加載執行導致無法正確定位廣告寫入的位置。對於阿裡媽媽廣告設置alimama_type=”i”的時候,載入圖片廣告是根據多層document.write實現的,只能正確渲染最後一個圖片廣告。復現如 DEMO 。
因為ControlJS的異步加載不存在任何依賴順序,所有腳本都是並行加載執行,如果你的頁面存在太多依賴關系,ControlJS將不會適合你的項目。
最後總結ControlJS為我們做了什麼事,利弊還需要自己去權衡:
原文:http://ued.taobao.com/blog/2011/03/18/controljs-alimama/
document.write = CJS.docwriteOrig;
CJS.docwriteOrig = document.write;
document.write = CJS.docwrite;