在介紹ensure內部的實現之前,讓我們先來看看其功能:
ensure({
html: "popup.html",
javascript: "popup.js",
css: "popup.css"
}, function() {
Popup.show("hello world");
}
);
在這段代碼中,ensure首先會確保popup.html、popup.js、popup.css這3個檔案的載入,如果都沒載入過ensure就會動態載入它們;如果已經載入過了,ensure不會再次載入。在確保這3個檔案都載入後,ensure會調用後面的匿名函數,也就是執行Popup.show("hello world");。
接下來,就讓我們看看ensure是如何動態載入JavaScript與CSS的。
載入JavaScript
在ensure當中,載入JavaScript分兩種情況來執行,也就是Safari與非Safari這兩種情況。
在IE、Firefox、Opera中載入JavaScript
在這三款瀏覽器中載入JavaScript,其實只需要建立一個script元素,把src指向要載入的URL,最後把script元素追加到head元素上,那就搞掂了。此項工作是在HttpLibrary.createScriptTag()中完成的。不過我們不僅僅要載入JavaScript,同時還需要知道它什麼時候完成載入,這可以通過script元素的onload事件或onreadystatechange事件來實現。
在Safari中載入JavaScript
因為Safari 2不支援onload或者onreadystatechange,所以只能手動通過XHR把URL讀去過來,然後再手動eval這段代碼,這就帶來了一個限制──只能載入本域的JavaScript檔案。在ensure當中,eval的工作是通過HttpLibrary.globalEval()來完成的。為了讓JavaScript代碼在全域(global)上下文中eval,ensure還是使用了建立script元素的方法,並將要eval的JavaScript置於其內,最後把script元素追加到head元素內。
細心的人肯定要問,為什麼HttpLibrary.globalEval()要如此設計,而非直接window.eval或者eval.call。這是因為,window.eval和eval.call都無法在IE6中實現和script標籤載入JavaScript代碼一模一樣的效果,這兩種做法的eval在IE6下仍然不是在全域上下文中執行的。搜尋一下你就會發現一些相關的討論,例如jQuery就曾經使用window.execScript()來完成此項任務。不過最終大家都發現添加script元素才是最好的跨瀏覽器解決方案,所以現在的jQuery和ensure都是如此實現的了。
載入CSS
相對於載入JavaScript而言,載入CSS就簡單多了,而且方法也是類似的:在head元素內直接加入link元素就可以了。這也正是loadCSS()所完成的工作。
實際上,ensure沒有確保CSS完成載入後再執行下去。這估計是因為瀏覽器都能夠在CSS載入完成後自動應用到頁面上,因此Omar AL Zabir就認為CSS的載入順序是無關緊要的,不過假如CSS載入速度實在太慢,其實還是會影響顯示效果的。
在IE6中載入CSS
這次需要特別照顧的是IE6,而非Safari。IE6在往head元素添加link元素時,必須在window的上下文中完成,因此添加link的函數通過call調用切換了上下文。
總結
實際上動態載入JavaScript與CSS都並不難,在大多數情況下只需要向head元素追加對應的子項目就可以了,只有Safari2和IE6這兩款古老的瀏覽器是需要特殊照顧的。
官方地址
ensure