在JavaScript的發展上,可說是因Ajax而翻身,然而,就跟其他功能差異性一樣,實現Ajax的非同步物件,早期也沒有標準規範,為了弭平種種瀏覽器差異性問題,開發者多半透過程式庫來撰寫Web前端。但十幾年過去了,XMLHttpRequest(XHR)已經標準化,XMLHttpRequest的功能也獲得許多增強,而且規範在XMLHttpRequest Level 1!咦?不是Level 2嗎?

XMLHttpRequest的前身

談到瀏覽器,Internet Explorer總是萬惡的代表,而在非同步物件的實現上,Internet Explorer也是不遵守標準的壞蛋?實際不然,若就維基百科中XMLHttpRequest條目中的記錄而言,非同步物件其實始於微軟,他們為了Exchange Server建立的Outlook Web Access概念,後來在MSXML程式庫第二版提出定義,並實現了IXMLHTTPRequest介面。

1995年的Internet Explorer 5.0搭載了MSXML程式庫,可在建立ActiveXObject實例時,指定'Microsoft.XMLHTTP',透過ActiveXObject作為包裹器來操作。Mozilla 後來在Gecko樣版引擎,仿造了類似的介面nsIXMLHttpRequest,行為上類似IXMLHTTPRequest介面,在JavaScript中,Mozilla透過XMLHttpRequest物件作為包裹器來操作nsIXMLHttpRequest的實現,就歷史順序來看,XMLHttpRequest反而是後來才出現的仿造品。

然而,在2002年Gecko 1.0中,XMLHttpRequest獲得了完整的實現,而使用Gecko的瀏覽器不少,因而帶動了Internet Explorer以外的主流瀏覽器上,採用XMLHttpRequest成了產業標準,但是,Internet Explorer的份額依舊不少,因而,在談到Ajax的文件上,總是必須要偵測有無window.XMLHttpRequest特性,然後,再決定要new ActiveXObject('Microsoft.XMLHTTP'),或者是new XMLHttpRequest()。

雖然,Internet Explorer到2006年的7.0後,也有了XMLHttpRequest,不過,實際上XMLHttpRequest早期並沒有標準化,各家瀏覽器的實作多少也都存在著差異性,而一些共同的功能交集上,就今日來看,也有許多的不便,像是缺少原生的二進位資料處理、常見的事件註冊,以及跨域請求等。

XMLHttpRequest的標準化

關於XMLHttpRequest的標準化,W3C在2006年開始著手進行,目標是在當時的XMLHttpRequest各家實現中,取得共通的集合。進一步地,由於HTML5概念的成形,W3C考慮進化XMLHttpRequest,並在2008年發布了新規格的草案(曾被稱為XMLHttpRequest 2,或是XMLHttpRequest Level 2),其中就包含了XMLHttpRequest的一些增強功能。

在2011年時,新版的XMLHttpRequest 2草案規範,納入原本進行中的XMLHttpRequest規範,而曾經出現的XMLHttpRequest Level 2,就被廢棄了,因此,原本的XMLHttpRequest規範,成了XMLHttpRequest Level 1規範,這時,XMLHttpRequest才算是有了正式標準。

目前來說,想要取得XMLHttpRequest的規範,可以在〈XMLHttpRequest Level 1(https://goo.gl/SJpYVu)〉這個入口頁面找到,而現行規格書的內容,我們可以連到〈XMLHttpRequest Living Specification(https://goo.gl/p56i2B)〉查看。

然而,在許多早期的文件,或者甚至是新文件中,可能還是會以XMLHttpRequest Level 2,來說明XMLHttpRequest的新功能,甚至以XMLHttpRequest Level 1,來直稱過去未標準化前的XMLHttpRequest,而且,在W3C的網站上,也確實還有寫著XMLHttpRequest Level 2的頁面,不過,那只是個規格書草案的快照,連接到該頁面時,也會出現頁面過期的訊息,如果按下其中的新頁面連結,也是連到XMLHttpRequest Level 1的入口頁面。

XMLHttpRequest Level 1的新功能

從先前談到的歷史來看,XMLHttpRequest Level 1發布非常多年,現代瀏覽器基本上都已經實現,包括微軟的Edge瀏覽器(Internet Explorer就算了),只不過,若開發者一直都是透過程式庫來做非同步請求,可能沒意識這些新功能的存在與實現。

例如,在事件的新功能上,XMLHttpRequest Level 1定義了onloadstart、onprogress、onabort、onload等方法,事件處理器的第一個參數會是ProgressEvent,而XMLHttpRequest的timeout屬性,也是該規格的定義之一,若請求逾時,可透過ontimeout獲得通知。

XMLHttpRequest也增加了responseType,可用來指定回應的類型。目前,這裡可設定的數值,有'arraybuffer'、'blob'、'document'、'json'與'text',預設值為空字串。而在伺服端回應之後,可透過XMLHttpRequest實例的response,取得對應的ArrayBuffer、Blob、HTML DOM、JSON物件與字串,而不用再嘗試透過responseText取得純文字後,再自行剖析為對應的物件了。

在過去,想透過XMLHttpRequest進行檔案上傳,是一件難事,因為各家瀏覽器有不同的(複雜)作法,或者是透過Flash來實作,但現在可以透過FormData收集檔案上傳欄位,直接設定為send方法引數來進行檔案上傳,此時,內容類型一定是multipart/form-data(不用,也不可自行設定內容類型),若須得知上傳進度,可以藉由XMLHttpRequest的upload特性,取得XMLHttpRequestUpload物件,透過註冊其onprogress,予以實現(XMLHttpRequest的onprogress是關於下載進度的事件處理器)。

至於跨域請求,過去基於安全性,而有同源策略(Same-origin policy)的限制,開發者為此想出了JSONP的方式,客戶端動態生成script標籤,而伺服端可以傳回特定的指令稿來繞過限制,然而,跨域請求的需求一直存在著,新規範中現在也就不需要特別的設定,可直接允許XMLHttpRequest跨域請求,但條件是伺服端支援跨域請求的協議(畢竟,JSONP也必須伺服端支援才有可能)。

例如,對於簡單的GET、POST請求,也就是內容類型屬於:application/x-www-form-urlencoded、multipart/form-data、text/plain之一的話,伺服端可以設置Access-Control-Allow-Origin回應標頭,來指定可允許的跨域請求來源,至於其他的協議,可參考規格書〈3.2. CORS protocol〉,若需要協議實作範例,我們可以參考〈Server-Side Access Control (CORS)(https://goo.gl/RDWMzx)〉的內容。

思考新規範下應有的樣貌

現在才在談XMLHttpRequest的新功能,就時間點來看,像是冷飯熱炒,畢竟早期談論XMLHttpRequest Level 2的文件,多半是在2012年左右了,不過,這也表示,這些功能在瀏覽器上,應該都有非常大程度的實現了,如果不考慮舊包袱,使用它們來撰寫相關功能,確實有很大的幫助。

實際上,不只是XMLHttpRequest,其他像是ECMAScript 6、HTML5等標準,都發布一段時間了,瀏覽器都有相當的實現,不過,也許是考慮太多相容性的關係,很少能看到這些技術使用時的應有樣貌,以及設計時的考量與討論,若不是我這陣子以此為出發點,實驗性地撰寫〈語言技術:ECMAScript Essence(https://goo.gl/yZkwSE)〉,也不會意識到它們的存在。

當然,我們可以用程式庫粉飾一切,只不過這是前端開發的本質嗎?也許該適時地拋棄一些舊包袱,重新就新規範來設計與實現,看看樣貌,對於前端開發日益龐大的複雜度,才能免除或減輕吧!

作者簡介


Advertisement

更多 iThome相關內容