原生 AJAX 入門

在使用 jQuery 開發了不少網站以後,對原生的 JS 是越來與生疏了,可是對於任何一門語言來講,框架只會讓事情事半功倍,但歸根結底,仍是語言原生的特性是根本。
  對於JS來講,基於瀏覽器這個宿主環境,其有不少尷尬的地方,例如兼容性很差,對於多種瀏覽器來實現兼容,致使代碼很臃腫;原生的不少方法屬性使用十分不便捷等。但對於一門語言要想深刻,掌握其原生的特性仍是十分必要的,畢竟相對於 jQuery 這種類庫或者框架來講,要知其然,還要知其因此然。
  來自維基百科的關於AJAX 的解釋是:Asynchronous JavaScript and XML(異步的JavaScript和XML技術,但其實AJAX的實現不必定和 XML 有關係,也即AJAX和數據格式無關),是一種建立交互式網頁應用的網頁開發技術,是多種技術的組合。目的就是讓前臺的數據交互變得更快捷,不用刷新頁面就能夠完成數據的更新。
  AJAX 的優勢很明顯,利於用戶體驗,在不刷新頁面的狀況下利用DOM交互能夠更新頁面內容,不會打斷用戶的操做,因爲其獲取和發送的數據量小的特色(獲取數據格式可使XML或者JSON或者其餘,不是整個頁面),減少服務器壓力也是它的一個優勢。
  而缺點,就是倍受人們關注的SEO問題(Google使用#!這種規定來支持AJAX的SEO,可是百度貌似尚未支持,因此作互聯網的人,若是用百度的話,我勸你趁早轉行,不要告訴我不穩定,不會那啥),前進後退問題(能夠用其餘方法解決HMTL5新增了相應的API),以及必須重視的安全問題等都是它現存的一些缺點。
  而AJAX 的應用範圍,這其實這是一個太寬泛的問題,各人及各項目都有不一樣的用處,只要是可以提高用戶體驗的地方,均可以使用AJAX,可是對於什麼是用戶體驗,又是一個寫幾本書都寫不完的東西了。舉例子來講,對於Gmail這類應用,其實能夠看作SPA(單頁面應用, Single Page Application)應用,因此整站都不須要刷新,而對於G+ 這種網站來講,雖然能夠不整站刷新,可是整站刷新其實並無什麼不妥的地方,而不整站刷新,反而帶來了頁面假死的現象。聽說知乎內部也有相似於G+的項目,可是我以爲這種應用,對於知乎來講,其實並不必定適合,在愈來愈快的網速這種狀況下,其實仍是整個頁面刷新來的舒服,固然了,這個只是我的觀點。而對於小面積更新數據來講,好比對用戶名或者郵箱惟一性(輸入框失去焦點後)等數據判斷、選項卡(選項卡切換AJAX加載)、彈出的登陸註冊窗口(能夠AJAX返回HTML結構)以及用戶提交表單信息後的反饋信息等等,這種形式確實很是合適的。
  來了這麼多開胃菜,其實不少人已經煩了,那麼咱們就開始來點乾貨,下面將經過一個新用戶註冊的過程來淺嘗輒止的講解下AJAX的基礎知識。
  註冊表單要求:用戶名爲郵箱,惟一性,密碼必須大於六位,爲了簡單起見,這裏不作過多合法性驗證。
  AJAX出現以前,若是要註冊新用戶的話,用戶必須填寫完全部必填信息,而後單擊提交按鈕,將數據提交到服務器以後,才能驗證數據的合法性,例如驗證郵箱惟一性,提交表單以後,必須通過服務器判斷郵箱的惟一性,而後刷新頁面在顯示此郵箱是不是惟一的,可用來註冊的(其實還能夠用隱藏iframe來實現)。而有了AJAX以後,當用戶將郵箱輸入完成,此輸入框失去焦點的時候,經過AJAX查詢服務器,而後給出郵箱是不是惟一的結果,雖然也通過了服務器的交互,可是頁面沒有刷新,而且交互的數據量很是小,發送的數據除了必要的頭信息就是查詢數據,而接受的數據除了頭信息以外,甚至能夠減小到只用數字0或者1來表示數據是否合法。
  來看HTML結構代碼:php

<form id="register" method="POST" action="register.php">
    <label>郵 箱:<input type="text" id="email" placeholder="請輸入郵箱地址"></label>
    <label>密 碼:<input type="password" id="password" placeholder="請輸入密碼"></label>
    <input type="submit" value="提交">
</form>

  除了表單頁面以外,還須要有一個後端頁面register.php來負責驗證數據的合法性以及註冊過程,這裏是後端頁面,就不給出具體代碼了。
  接下來就交給AJAX來實現了,當郵箱輸入框失去焦點時使用 GET 方式來和後臺交互以驗證此郵箱的惟一性。
  而當點擊提交按鈕時,使用 POST 方式來和後臺交互,以驗證是否可以註冊成功。
  除了HTML和PHP以外,接下來就是AJAX發揮做用的時候了。
  提到AJAX,就不得不提XMLHttpRequest(XHR) 對象,它是W3C的一個標準,也是AJAX最核心也是最底層的對象,可是悲劇的是IE系列,直到IE7+ 才實現了W3C的標準XHR對象,可是咱們仍是要支持IE6的啊(萬惡的IE6,這裏不得不說,其實不少地方都誤解了微軟,在IE5中微軟就實現了XHR對象,後來各個瀏覽器廠商才逐漸的實現此對象,只不過IE5/IE6是按微軟本身的方式來實現的,使用的是ActiveX對象),因此不得不作兼容性處理。
  爲了兼容IE6,那咱們就寫兼容性的代碼吧。html

function creatXHR(){
    if (window.XMLHttpRequest) {
        return new XMLHttpRequest();
    } else if(window.ActiveXObject) {
        return new ActiveXObject("Microsoft.XMLHTTP");
    } else {
    alert(‘此瀏覽器不支持 XHR !’);
    }
}

  使用此代碼來建立一個建立兼容XHR對象的函數,第二行先判斷window是否有XMLHttpRequest這個屬性,若是有則證實是支持W3C標準的XHR對象,不然就是IE6了(已經能夠不用考慮其餘瀏覽器了),使用微軟的方式來建立XHR對象,所幸的是其餘瀏覽器的實現和IE早期的實如今內部是兼容的,W3C也兼容(其實W3C的不少標準都是採納於各個瀏覽器已經實現的事實標準)。
  咱們先來想一想數據驗證的過程,獲取數據,建立XHR對象,而後使用XHR對象的方法來與服務器交互。先無論具體的交互過程,無論是數據驗證仍是註冊,都是這麼三步,那麼接下來就實現另外的函數。
  發送數據並驗證:前端

function validateData(obj,method,url,data){
    var xhr      = creatXHR(),
        iscancel = setTimeout(function(){xhr.abort();console.log("超時取消!");},5000); // 設定超時取消

    xhr.onreadystatechange = function() {  //綁定事件
        if (xhr.readyState == 4 && xhr.status ==200){ //此時數據可用

            var res = (window.JSON)?(JSON.parse(xhr.responseText)):(eval("("+xhr.responseText+")"));//解析 json字符串

            obj.nextSibling.innerHTML = res.msg;

            if (res.code == 0 || res.code == 2) {
            obj.nextSibling.style.color = 'red';
        } else {
        obj.nextSibling.style.color = '#5B636A';
        };

        clearTimeout(iscancel);//若是響應成功,取消定時函數

        }
    }

    //檢測 Method 方法,GET POST
    if (method.toLowerCase() === "get") {
        xhr.open("GET",modiUrl(url,data),true);
    xhr.send(null);
    } else if (method.toLowerCase() === "post"){
    xhr.open("POST",url,true);
    xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
    xhr.send(serialize(data));
    };      
}

  而後來詳細闡述一下這個xhr對象的相關內容(獲取輸入數據就是很正常的獲取數據的過程,這裏很少說了)。
  對於vliData() 函數中的XHR而言(並不僅是此函數中,全部的XHR對象都同樣,後面相同,此處只是爲了好說明),要使用XHR對象時,必須調用的第一個方法是open(),即:xhr.open();此方法須要接收三個參數:
  第一個參數是要發送的請求的類型(GET POST等),
  第二個參數即請求的 URL,GET請求能夠將查詢參數追加到URL的末尾,可是每一個參數的名值對必須通過encodeURIComponent() 編碼纔可以使用,否則容易發送錯誤(雖然通常狀況下參數的名稱都爲拼音或者英文,可是仍是應該養成每次編碼的好習慣)。
  第三個參數即表示是不是異步發送請求,布爾值,true或者false。上例中已有使用,即:web

xhr.open("GET","register.php?"+encodeURIComponent('email')+"="+ encodeURIComponent(email),true);

  這裏使用了 get 方法,異步獲取register.php 這個url的數據(url必須同域同端口同協議,不然報安全錯誤,能夠絕對路徑或者相對路徑)。可是當調用open() 方法的時候並不會真正的發送請求,而是啓動一個請求以備發送。而要發送請求,就必須調用send()方法才行。例如上面的代碼所示,即:ajax

xhr.send(null);

  這裏的send()必須接受一個參數,即:要做爲請求主體發送的數據。若是不須要經過請求主體發送數據,則必須傳入 null ,由於不傳入的話,有些瀏覽器會報錯。
  好了,數據發送完畢了。
  數據發送完畢,等到服務器處理完畢並返回,瀏覽器收到響應的時候,相應的數據就會自動填充XHR對象的相應屬性。XHR對象的相應屬性主要包括如下幾種。
  responseText : 做爲響應主體被返回的文本。
  responseXML : 若是響應的內容類型是text/xml 或者 application/xml 的時候,這個屬性保存這響應數據的XML DOC 文檔。
  status : 響應的http狀態。
  statusText : http狀態的說明
  在接收到響應以後,第一步就是要檢查 status 屬性,以肯定響應已經成功返回,通常來講,能夠將http的狀態碼爲200時做爲成功的標誌(其實還有304等),此時responseText 的內容已經就緒,若是內容類型正確的話,responseXML 也已經能夠訪問。
  可是咱們這裏使用的是異步請求,不像同步請求那樣會將JS執行阻塞,當收到響應後纔會執行後面的代碼。所以要經過事件來肯定XHR的狀態,由於XHR對象的open() 或者 send() 方法被調用以後,並不清楚XHR 到達什麼狀態了,因此還要判斷 XHR 的狀態決定以後該如何處理。
  對於 XHR 狀態的判斷,能夠檢測 XHR 對象的 readyState 屬性,該屬性表示 XHR 對象請求/響應過程的當前活動階段,可取值以下:
  0 : 未初始化,即還沒有調用 open() 方法,
  1 : 啓動,已經調用open() 方法,但還沒有調用 send() 方法,
  2 : 發送,已經調用send() 方法,但還沒有接收到響應,
  3 : 接收,已經接收到部分響應數據,
  4 : 完成,已經接收到所有響應數據,便可以在客戶端使用數據了。
  同時,XHR 對象還有一個事件readystatechange ,而readyState 屬性的值每變化一次,都會觸發一次readystatechange 事件,所以能夠經過檢測此事件每次觸發時readyState屬性的值,通常狀況下,咱們只對readyState的值爲 4 時的狀況感興趣,由於此時全部的數據已經就緒可用,不過爲了確保瀏覽器的兼容性,必須在調用 open() 以前指定readystatechange事件處理程序。如上代碼所示。json

xhr.onreadystatechange = function() {
    if (xhr.readyState == 4 && xhr.status ==200){
      alert(xhr.responseText);
  }
}

  在onreadystatechange事件每次觸發時,檢測readyState的值,若爲 4 ,且status 爲200(http狀態,前面已說明)時纔可使用此數據。
  另外,在接收到響應以前,還可使用 abort() 來取消異步請求。例如當請求相應時間超長時,能夠調用此方法,而後將其異步請求取消。如:後端

var iscancel = setTimeout(function(){xhr.abort();},5000);
xhr.onreadystatechange = function() {
    if (xhr.readyState == 4 && xhr.status ==200){
        alert(xhr.responseText);
    clearTimeout(iscancel);
  }
}

  便可在超過5秒鐘未收到響應,因爲超時而取消本次請求。
  若是請求發送成功,而且也接收成功的話,responseXML或者responseText也就可用了,此時能夠按返回不一樣的數據來進行處理。目前爲了開發簡便起見,通常返回的就是html結構或者json數據,關於數據的處理和使用,這裏就不涉及了。
  對於 HTTP 來講,每一個請求和響應都會包含相應的頭部信息,有的有用,有的沒用,同時 XHR 也提供了相應的方法來操做這兩種頭部的信息,由於其用處不是很是大,因此這裏就再也不展開了(不過有時候能夠經過自定義的頭部信息加上後端的配合來實現一些額外的功能),只是在使用 POST 方式請求的狀況下,須要用到setRequestHeader() 方法。
  以上就是一個完整的利用原生的 JS 來寫的GET 方式下的 AJAX請求,而POST 方式下的請求咱們尚未接觸到。那麼咱們下面就開始闡述一下POST方式下原生 JS 的AJAX請求的實現方法。
  前面已經說過,XHR對象調用的第一個方法是open(),open() 方法的第一個參數是請求類型,因此POST 方式的 open() 方法即爲:瀏覽器

xhr.open("POST","register.php,true);

  而第二步就是將發送的數據經過 send() 方法發送出去(前面已經講過)。可是服務器端對於提交的web表單必須通過處理,由於對於POST提交的非表單數據,其處理方式是不一樣的,也就是後端程序必需要讀取前端發過來的原始表單數據,並從中解析出有用的部分,所以咱們必須經過XHR模擬表單數據的提交,這也就用到了自定義頭部信息。爲了保證兼容性,使自定義頭部發送成功,必須在open() 方法以後,在 send() 方法以前,設定自定義頭部(若是不設定自定義頭部的話後端是否能夠獲取提交的數據呢,答案是能夠,可是就不能使用$_POST這種超級全局變量,而只能經過$HTTP_RAW_POST_DATA來獲取了,此處基於PHP而言)。安全

xhr.open("POST","register.php,true);
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
xhr.send(data);

  自定義頭部的意思就是表單提交時的內容類型。
  而後就是要發送的數據了,由於最初XHR設計主要是爲了處理 XML,因此這裏能夠傳入XML DOM文檔,傳入的文檔在通過序列化以後做爲請求主體被提交到服務器,固然了,這裏也能夠傳入任何想發送到服務器的字符串。咱們這裏使用提交字符串方式。
  因爲 POST 數據的格式與查詢時(GET)字符串格式相同,因此只須要將表單中的數據進行序列化,而後經過XHR發送到服務器便可。服務器

xhr.onreadystatechange = function() {
    if (xhr.readyState == 4 && xhr.status ==200){
        alert(xhr.responseText);
    }
}
xhr.open("POST","register.php,true);
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
xhr.send(data);

  而後就是對錶單數據的序列化了。其實和GET數據格式相同,即email=a@b.com&site=yimity.com,可是要編碼。這裏就很少作討論了。
  最終的代碼爲:

var data = "email=yimity%40gmail.com&password=123456";
xhr.onreadystatechange = function() {
    if (xhr.readyState == 4 && xhr.status ==200){
        alert(xhr.responseText);
    }
}
xhr.open("POST","register.php,true);
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
xhr.send(data);

  經過一個例子,將AJAX最基本的 GET 和POST 講清楚了,其實AJAX還能夠用來上傳文件,不過這都須要支持HTML5的瀏覽器纔可實現(隱藏的iframe也能夠實現,但不屬於純AJAX了),HTML5 中的 XMLHttpRequest 2 級實現了不少XMLHttpRequest 1級中的細節,而且實現了不少很是有用的API,例如文件上傳, FormData 等,可是這些不屬於基礎的AJAX交互範疇,因此這裏就不涉及了。
  
  打完收工,其實代碼這東西,仍是例子最重要,不過個人代碼很醜。點此查看

相關文章
相關標籤/搜索