前兩部分介紹了三種方法創建可重用的天氣面板。這些方法都採用了 Asynchronous JavaScript™ + XML (Ajax) 技術,特別是 JavaScript XMLHttpRequest
對象來實現天氣面板庫。這些方法都使用了某種形式的 Web 代理將 XML 天氣數據從 National Weather Service (NWS) XML 搬到我的服務器上,以避免 Ajax 的同一域問題。
本系列的 第 1 部分 詳細討論了同一域 問題,出於安全上的考慮將 XMLHttp
請求限制到提供原始 Web 頁面的同一臺服務器上。實際上,很多 Ajax 應用程序都需要超出一臺服務器範圍之外的其他數據。因此設計的時候必須確定訪問 Web 服務器配置(比如創建 Apache ProxyPass
規則)還是創建專門的服務器腳本。
實踐證明,還有另一種辦法。它避免了同一域問題,程序員不需要處理上面所述的任務。
|
詳細討論天氣面板庫的最後一種實現之前,首先介紹一下所需的工具:
Yahoo! Pipes 是一種基於 Web 的工具,可以聚合能夠通過 Web 訪問的不同類型的數據,比如 RSS 提要。Yahoo! Pipes 使用圖形化編輯器(如 圖 1)編輯器創建,該編輯器可在標準 Web 瀏覽器中運行,如 Windows® Internet Explorer® 或 Firefox。
可通過把模塊從工具面板上拖拉布置到畫布上來創建管道。模塊輸出到另一個模塊輸入之間的管道將模塊聯繫起來。
完成的 Yahoo! Pipe 有一個惟一的 URL。訪問該 URL 時,Pipe 邏輯在 Yahoo! 服務器上執行。服務器處理所有的數據訪問,執行數據操作並輸出結果。默認情況下數據以 RSS 格式輸出,但是允許在 URL 中指定其他數據格式,包括 JSON。很快將看到能夠以 JSON 格式輸出數據這一點至關重要。
天氣面板模塊使用的 NWS 數據可作爲 Yahoo! Pipes 的數據源。四字符 NWS 站點 ID 可作爲 Yahoo! Pipe URL 的參數。
圖 1 顯示了最後一種天氣面板實現所使用的管道。雖然 Yahoo! Pipes 能夠完成非常複雜的數據處理功能,但這裏只有一個目的:將 NWS 數據從 XML 轉換成 JSON。
前面已經多次提到過 JSON。它是什麼,我爲何要在天氣面板應用程序中使用呢?
JSON 即 JavaScript Object Notation,JavaScript 語言本身支持的一種數據格式。和需要 JavaScript 程序解析的 XML 不同,JSON 就是 JavaScript 代碼。
作爲 JSON 和 XML 的比較,首先看看弗吉尼亞州 Richmond NWS 數據的 XML 版本,如 清單 1 所示。
清單 2 是這些數據的 JSON 版本。
簡單地說,JSON 就是一種 JavaScript 數據結構。上面清單中的 JSON 可以方便地賦給 JavaScript 變量然後引用,如 清單 3 所示。
清單 3. 在 JavaScript 代碼中訪問 JSON 數據結構
var weather = { "count": 1, "value": { "items": [ { "location": "Richmond International Airport, VA", ... } ] } }; alert (weather.value.items[0].location); |
上述 JavaScript 代碼片段將縮減後的 JSON 數據賦給 JavaScript 變量 weather
。location
元素作爲 JavaScript alert()
函數的參數引用。地點數據顯示在彈出的警告窗口中,如 圖 2 所示。
和訪問等價的 XML DOM 結構相比,多數 JavaScript 程序員認爲引用 JSON 數據結構更簡單更直觀。這也足以說明爲何在 Ajax 應用程序中這種數據結構越來越普遍。
但是 JSON 吸引 Ajax 開發人員還有另一個原因:通過某種 JavaScript 技巧,JSON 可以避免代理數據。
前述三種天氣面板庫的實現方法有一個共同的特點。瀏覽器中運行的 JavaScript 程序必須(通過 XMLHttp
請求)從我自己的 服務器上請求數據。這是由於 XMLHttp
請求的同一域限制造成的。
此前有兩種方法使用 Apache ProxyPass
規則來解決這個問題,還有一種使用專用的服務器端腳本從 NWS 服務器上獲取數據。
但是如果不能改變服務器配置或者不能運行服務器端腳本怎麼辦呢?
有一種技術允許 JavaScript 程序訪問其他域中服務器上的數據。這種技術有時候被稱爲腳本標記 hack。
爲了說明其工作原理,首先來看看 HTML 腳本標記。通常嵌入 Web 頁面作爲包含 JavaScript 代碼庫的一種辦法,如 清單 4 所示。
<html>
<head>
<title>An Example</title>
<script language="JavaScript" src="some_javascript_code.js"></script>
|
清單 4 顯示了一個 Web 頁面中的前面幾行。最後一行的腳本標記說明,將從服務器上加載名爲 some_javascript_code.js 的 JavaScript 源文件。src
屬性指定了腳本位置的 URL。該例中的腳本位於同一臺服務器上,但並非必須如此。可以包含任務服務器上的 JavaScript 代碼。
這就意味着使用腳本標記可把 JSON 數據加載到瀏覽器中,後者恰恰是 JavaScript 代碼。對於天氣面板應用程序來說似乎意義不大,因爲腳本標記本身就是打開頁面時加載的 HTML 的一部分。對於 Ajax 解決方案,需要在頁面加載之後 發送服務器請求。
不過 JavaScript 代碼可以操縱 Web 頁面 DOM。可以添加和修改 Web 頁面中的數據。如果在 JavaScript 代碼中動態創建腳本標記並添加到 DOM 中會怎麼樣呢?結果如 清單 5 所示。
var head = document.getElementsByTagName("head").item(0); var script = document.createElement ("script"); script.src = "some_javascript_code.js"; head.appendChild (script); |
執行後將在當前頁面的 head
標記中增加一個新的 script
標記。它相當於 清單 4 中的 HTML 片段,只不過 some_javascript_code.js 中的 JavaScript 代碼是在頁面加載之後又加載的。
您可能懷疑這對於天氣面板庫來說有什麼價值。假設 some_javascript_code.js 文件的內容如下:
alert ("Hello!"); |
一旦 JavaScript 文件加載到瀏覽器中, JavaScript 解釋器就會運行包含的任何可執行代碼。因此會立刻顯示一個警告窗口。
由於從 Yahoo! Pipes 生成的 JSON 數據是 JavaScript 代碼,所以也可通過上述技術動態加載。但 Yahoo! Pipes JSON 數據不是可執行 JavaScript 代碼,而僅僅是一種 JavaScript 數據結構。可以加載到瀏覽器,但是如何知道什麼時候加載的?
在創建動態標記之前設置某種定時器,然後假定 JSON 在某個時間,比如說 3 秒之後到達,這種方法初看起來似乎可行。從好的方面說這種方法不可靠,而且實踐證明這是不必要的。
每個 Yahoo! Pipe 都有惟一的 URL。清單 6 顯示了 NWS 管道的 URL。
http://pipes.yahoo.com/pipes/pipe.run? &_id=CI6HgSh43BGP8nmJouNLYQ &textinput1=KRIC &_run=1 &_render=json &_callback=myfunction |
爲了更清楚的看到不同的參數,我將 URL 分成了幾行。_id
參數標識了這是我使用的管道,textinput1
給出了 NWS 站點 ID。_run
參數說明希望執行該管道,_render
告訴 Yahoo! Pipes 服務器以 JSON 格式輸出數據。
最後一個參數 _callback
做什麼呢?它告訴 Yahoo! 服務器將數據封裝到指定的函數調用中,即 myfunction()
。Yahoo! Pipes 返回 JavaScript 程序的結果如下:
myfunction ( {"count":1, ... } ); |
爲了簡化我省略了大部分 JSON 數據,但道理您應該清楚了。和前面加載包含 alert()
調用的 JavaScript 文件一樣,JavaScript 解釋器執行上述代碼,調用 myfunction()
並把請求的全部數據傳遞給該函數調用。
利用回調函數完全不需要知道服務器什麼時候完成 JSON 數據的加載。
那麼回調函數中有什麼?對於我的天氣面板庫,它完成天氣數據的格式化,和方法 1 中的 XMLHttpRequest
響應函數非常類似。在方法 1 中,我從 responseXML
DOM 對象中提取需要的數據。這裏只需要訪問 JSON 數據結構,如 清單 7 中的 jsonHandler
方法所示,這就是天氣面板 JSON 方法的全部代碼。
清單 7. 天氣面板庫的 JSON/Yahoo! Pipes 實現:weather_badge_ypipes_json.js
和前面的所有方法一樣,也實現了 weather_badge()
函數。並向該函數傳遞四字符的 NWS 站點 ID 和 HTML DIV
標記名稱。DIV
標記就是呈現天氣面板的頁面區域。
其他代碼定義了名爲 YPipesWeather
的 JavaScript 對象。該對象包括三個函數:構造器、動態創建腳本標記(從而運行 Yahoo! Pipe)的方法和一個回調函數。
結果是一個 JavaScript 庫,雖然沒有用 XMLHttpRequest
對象,但行爲和前面三種方法相同。
圖 3 顯示了最後這種方法的數據管道。除了最開始打開頁面,不需要再訪問我的 Web 服務器。
清單 8 顯示了利用天氣面板庫的 JSON 版本的示例 HTML Web 頁面。