ajax javascript xmlhttprequest
這是一個使用在本部落格的技巧,用於因應樂多部落格系統之限制。
傳統上,我們執行 JavaScript 程式的方式是靜態的,在 HTML 文件中以 <script type="text/javascript" src="example.js"></script> 的敘述載入並執行外部 JavaScript 程式。若我們無法使用上述方式時,就需要動態載入與執行。另一種需要動態載入與執行 JavaScript 程式的時機為,存在幾個相當龐大的 JavaScript 程式但不全部都用得到的情形,為了降低瀏覽器載入的時間,此時我們需要依某些條件決定那些 JavaScript 程式需要載入。
欲於 JavaScript 程式中動態載入其他 JavaScript 程式必須利用 XmlHttpRequest 才可自伺服器取得其他 JavaScript 程式的內容,接著再調用 window.eval(responseText) 執行載入的程式內容。基本上不是很困難的動作,但實際使用時卻有一個很大的效能缺陷。如果外部 JavaScript 程式存在執行順序的相依性,則我們通常須以 XmlHttpRequest 的「同步」模式取得外部 JavaScript 程式內容,但這卻將帶來非常明顯的瀏覽器停滯現象。在同步模式下,瀏覽器為了等待文件完成載入動作會擱置其他檔案的載入動作。當外部 JavaScript 程式不只一個時,使用者將明顯地感受到瀏覽器的停滯現象。
在實踐過程中,我發現大多數瀏覽器以 <script type="text/javascript" src="example.js"></script> 的敘述靜態載入 JavaScript 程式時,採非同步載入但依序執行的策略。所以載入外部程式的動作不會產生明顯的停滯現象。因此實踐動態載入 JavaScript 程式之需求時,我們需「非同步」載入外部程式,但「同步」依序執行,以避免瀏覽器的停滯現象。
我的實踐概念是以 XmlHttpRequest 的非同步模式取得所有外部 JavaScript 程式的內容。欲載入的 JavaScript 程式之位址置於 scripts 陣列中,載入後的程式內容存於 loadedScripts 陣列。再以 Interval 設定時間間隔探詢是否已完成所有外部 JavaScript 程式的載入動作 (loadingScripts == 0。容我提醒,因為我們以非同步模式於載入主文件時並行載入外部 JavaScript 程式。在非同步模式下,我們不能肯定是主文件先下載完成亦或外部檔案先下載完成,故不能用 window.onload 。
當所有外部程式皆已載入後,再依 scripts 的順序,執行載入的程式。執行程式必須使用 window.eval() 才能將執行時產生的個體置於 window scope 中 (簡單說即令那些個體成為全域變數) 。
下列是實作範例。示範動態載入 Yahoo UI Library 的內容之後,再以 Yahoo UI Library 的 API 設定 "Hello world" 的位置。這也可以示範為何我們必須用 window.eval() ,如果將 window.eval() 改為 eval() ,則稍候的 YAHOO.util.Dom.getXY() 動作會發生錯誤,錯誤訊息為 YAHOO 個體未定義。此因單純用 eval() 執行時所定義的 YAHOO 個體不是處於 window scope 中,而是在一個 local scope ,故 JavaScript 會找不到 YAHOO 個體。