1. 程式人生 > >Ajax基礎理解

Ajax基礎理解

Ajax也是前端必備技能了,學習任何語言,都需要以理論為基礎的大量實踐才能真正學會,之前學了Ajax很多遍,因為缺乏大量實踐,總是會忘。所以不實踐是失敗之母。。。當然理論基礎也很重要啦,今天談談我對Ajax的基礎認知。

定義:全稱:Asynchronous JavaScript and XML(用非同步的形式的JavaScript去操作XML) 用來傳輸進行資料互動 其實就是拿資料發資料。

作用: 傳統的資料提交,大多是通過表單的形式,填寫資料,點選提交,資料就會被提交到後端,這種提交往往會跳轉頁面,存在很多問題,比如我們填完資訊點了提交,就會跳到後端頁面,後端頁面進行驗證,發現使用者已經註冊了,這個時候又會跳轉到表單填寫頁面,讓我們回去重新填,那之前填的全沒了,肯定體驗很不好。而Ajax就是能做到私底下去請求頁面傳遞資料,然後返回給我們請求的結果,我們前端接收到這個結果,根據結果可以做一些操作,比如提示錯誤資訊,整個頁面是一直處在表單填寫介面,從未跳轉。想想我們平常註冊帳號的時候,是不是沒跳轉介面就知道自己哪填的不對了,就是ajax在起作用。

下面我們先不解析具體的原理,先來看一個大致的實現過程。

首先,你得知道是把程式碼放在伺服器下執行的,開啟的時候不能用本地的地址,要用localhost/。。這種形式才是在服務下訪問的。要是這不瞭解的話,可以先自行百度下。我平常都是用的xampp第三方整合的伺服器,比較輕便。

需求:新建一個文字1.txt,隨便輸入什麼內容,新建HTML頁面,點選HTML頁面裡的按鈕發生請求獲取1.txt裡面的內容。這裡只看js程式碼。

複製程式碼
var oBtn = document.getElementById(‘btn’);
oBtn.onclick = function(){
var xhr = new XMLHttpRequest(); //建立Ajax物件
xhr.open(‘get’,‘1.txt’,true); //設定請求資訊
xhr.send(); //提交請求

    //等待伺服器返回內容
     xhr.onreadystatechange = function() {
            if ( xhr.readyState == 4 ) {  //判斷當前的請求狀態,4代表後端已經處理完成
                   alert( xhr.responseText ); //彈出內容
               } 
      } 
 } 

複製程式碼
當點選按鈕的時候,你就會發現彈出了1.txt裡面的內容。

我們來具體解析一下這個步驟

var xhr = new XMLHttpRequest(); //建立Ajax物件
我們要用Ajax獲取資料,首先呢,要建立一個Ajax物件,就跟你想獲取系統時間要建立一個時間物件是一個道理。物件的名稱就是 XMLHttpRequest(),建立好之後我們就可以用物件下的方法屬性進行資料互動了。

需要說明的是,這個物件實際是存在相容問題的,IE6以下沒有這個物件的,所以是獲取不到資料的,IE6以下用的實際是一個外掛的方式:

ActiveXObject(‘Microsoft.XMLHTTP’)
//ActiveXObject: IE6下外掛的總稱,包含很多外掛
//Microsoft.XMLHTTP:具體某個外掛的名字
所以我們需要對上面做一個相容性的處理:

複製程式碼
var xhr = null;
if(window.XMLHttpRequest){ //加window是因為如果直接判斷IE下不存在的東西會報錯,加了window,就是在判斷一個屬性是否存在,這樣就不會報錯了(當然我們都知道所有的東西都在window物件下,所以這樣判斷是有效的)
xhr = new XMLHttpRequest();
}else{
xhr = new ActiveXObject(‘Microsoft.XMLHTTP’);
}
複製程式碼
接著看

xhr.open(‘get’,‘1.txt’,true); //設定請求資訊
Open方法:有三個引數
1、提交方式 Form-method(get/post)
2、提交地址 Form-action
3、是否非同步,是為(true)
首先講提交方式:get/post 這兩種的區別。

這裡我們不用ajax的方式,先直接通過傳統的表單提交資料來分析。

傳統的表單提交就是在表單裡面設定提交的一些引數,使用者的輸入資訊點選提交,會跳到指定的後臺頁面。

表單form標籤的屬性:
action:提交到哪裡 預設是當前頁面
method:提交方式 預設是Get
enctype: 提交的資料格式,預設是application/x-www-form-urlencoded

我們來具體看個get方式請求的栗子,並且瞭解前後端到底是怎麼互動的。

栗子需求:建立HTML頁面,PHP頁面,填入資料,點選提交,然後輸出我們輸入的內容。

HTML頁面:

1.get.php(如果不瞭解PHP語言,大概看下面的註釋簡單知道幹啥就行了)

複製程式碼

<?php header('content-type:text/html;charset="utf-8"'); //設定編碼格式,以及文件型別 error_reporting(0); $username = $_GET['username'];//獲取get請求的方式得到填寫的資料 這裡$_GET裡的引數是要和前端標籤中的name屬性是保持一致的 $age = $_GET['age']; echo "你的名字:{$username},年齡:{$age}"; //輸出內容 複製程式碼 觀察實驗結果,當點選按鈕,頁面會跳到1.get.php頁面,把內容輸出。並且觀察上面的位址列,會發現我們輸入的資訊被放在了位址列上。 這裡就要講一下網路請求方面的知識,當我們輸入網址獲取內容,整個過程是怎麼發生的呢?瀏覽器又做了什麼? 首先要講一下HTTP協議,雖然表面上我們通過程式碼來實現了互動,實際上在網路請求的時候內部是通過各種協議達到了想要的結果,這個協議呢就是一種規範,因為網路上存在太多不同型別的東西,要想互相能交流傳遞就需要一種共同遵守的約定,才能理解。而我們這裡就用到了HTTP協議,希望有機會的人已經要去學習一下計算機網路方面的東西,這裡推薦一本書《圖解HTTP》,是我看過最清晰易懂的書了,其實有一個系列,都可以看看。 所以當我們輸入網址的時候,瀏覽器根據HTTP協議生成一個請求報文傳送給服務端,然後服務端收到這個報文就會進行處理然後迴應給我們一個響應報文。這兩個報文裡面含有請求和迴應的各種資訊。其實從程式碼角度來看,請求報文是通過前端程式碼可以設定的,比如何時傳送傳送給誰,響應報文就是後端程式碼處理後返回的一個結果。 請求報文裡一般包括請求的方法(GRT/POST),請求的URL或傳遞的資料等。 響應報文裡有請求狀態碼,比如200表示請求成功,還有返回的一些資料等。 具體我們不說太多,可以自己私下多瞭解,,主要來說的是GET請求方式和POST的不同在請求報文上的體現。 其實整個GET請求過程如下,假設後臺語言是PHP。 1 輸入使用者資訊,點選提交,跳到指定的後臺的頁面. 2 GET方式會把使用者輸入的資料名稱和對應的值以這樣的格式(username=value&age=value )串連起來,放在指定的後臺頁面的位址列的問號(?)後面。並且把請求資料放到了請求報文裡。 3 後臺的程式碼 ,可以通過PHP語言中的$_GET方法,獲取到請求報文的使用者資訊,用$_GET['username']; $_GET['age']方法獲取到值;然後就可以進行一系列邏輯處理。 由此,我們可以知道GET方式: 1 把資料名稱和對應的值串連(username=value&age=value ),然後把資料放到指定頁面的url地址?後面,然後資料會被加入到請求報文中傳送給後端。所以我們完全可以在後臺頁面的位址列上手動更改資訊,相應的請求報文就會發生變化,GET方式請求的資訊在請求報文裡也看得到,所以get方式是不安全的,一般不用來提交比較敏感資料,大部分是獲取資料的時候用。 2 url有長度限制,所以GET請求的方式有資料量限制,每個瀏覽器都不同,所以不要用這種方式傳遞過長的資料。不然會被擷取,導致傳遞資料不完整。 3 只能傳遞字串型別 因為是使用者名稱是中文的時候,會預設編碼了,所以username後面是亂碼。 下面來看看POST方式,同樣是這樣的需求 HTML頁面: 1.get.php 複製程式碼 <?php header('content-type:text/html;charset="utf-8"'); error_reporting(0); $username = $_POST['username'];//不同的請求方式方法不同,$_POST方法專門用來獲取POST方式請求的資料 $age = $_POST['age']; echo "你的名字:{$username},年齡:{$age}"; 複製程式碼 我們看到輸出資訊頁面,位址列上沒有使用者資訊了,我們開啟開發者工具,找到網路就可以看到我們的請求報文,點進去就是具體內容,看上面的第二個圖,可以看到一些請求資訊,有請求的編碼格式,還有請求地址等,POST請求的資料實際是在請求報文的實體裡進行傳遞的,所以更安全些,但是我們發現我們通過控制檯還是可以看到提交的資訊,所以其實它也沒有很安全,一般還是要對輸入的資訊進行加密。 我們看第三張圖,可以看到請求的資料,上面的其實是瀏覽器已經按照某種格式輸入的資訊,下面的原始碼才是實際傳遞的資訊,可以看到串連的格式和GET請求是一樣的,使用者名稱後面的亂碼是編碼了,當傳遞的是中文的時候,才會進行這樣的編碼。 由此我們可以知道 Post請求 1 資料的串連格式和Get請求是一樣的。 2 通過請求報文的資訊,通過瀏覽器內部傳輸,不會表現在網址上。 3 傳輸資料量 Post理論上無限 4 可以傳遞多種資料型別(文字型別,二進位制)。 關於Open方法的第一個引數提交方式就講到這裡,第二個地址先簡單瞭解,下面來看第三個引數關於非同步和同步。 同步:就是一種阻塞模式,比如程式碼var a =1 ; alert(a);這就是一種同步,必須執行了第一種var a =1,你才會彈出a的值。 缺點:一般當你後面的程式碼需要用到前面的東西的時候 適合用同步,如果兩句程式碼完全無關,那用同步就沒有必要了。 非同步:就是一種非阻塞模式,最明顯的例子,就是定時器,當我們寫了一個30s後執行的定時器的時候,在30S內定時器後面的程式碼是可以執行的,而不是過了30s後面程式碼才能執行,這就是一種非同步。 缺點:當你後面的程式碼需要用到前面的東西的時候 如果用非同步,那麼後面的程式碼會在前面還沒載入好就出來,可能會有問題。 解決:當你後面的程式碼需要用到前面的東西的時候,可以用條件判斷來決定這些程式碼的執行,如果條件達成了就可以執行。 還拿上面的舉個栗子 複製程式碼 var oBtn = document.getElementById('btn'); oBtn.onclick = function(){ var xhr = null; if(window.XMLHttpRequest){ xhr = new XMLHttpRequest(); }else{ xhr = new ActiveXObject(‘Microsoft.XMLHTTP’); } xhr.open('get','1.txt',true); //設定請求資訊 xhr.send();//提交請求 //等待伺服器返回內容 這裡後面會具體講,大概就是監聽伺服器的狀態,先簡單瞭解即可 xhr.onreadystatechange = function() { if ( xhr.readyState == 4 ) { //如果內容響應成功,並解析完成 alert( xhr.responseText ); //彈出內容 } } } 複製程式碼 在上面的程式碼中xhr.send()提交請求是需要時間的,所以必須要等到一定時間提交成功後,我們後面的才能正確獲取到內容,所以這就是後面的程式碼正確執行,依賴於前面, 但是如果用同步的話,我們後面那些不依賴這些前面程式碼的程式碼也沒辦法執行,體驗就不好了,所以我們選擇用非同步,而對於這些依賴前面程式碼執行的程式碼,我們就進行判斷 if ( xhr.readyState == 4 )就是判斷如果資料響應到了,收到了,再彈出內容。(如果我們不判斷,按照非同步的原理,就會立馬彈出來,獲取資料需要時間,因為實際還沒獲取到資料,所以會彈出空,怕誤解,所以這裡我再強調下). 複製程式碼 var oBtn = document.getElementById('btn'); oBtn.onclick = function(){ var xhr = null; if(window.XMLHttpRequest){ xhr = new XMLHttpRequest(); }else{ xhr = new ActiveXObject(‘Microsoft.XMLHTTP’); } xhr.open('get','1.txt',true); //設定請求資訊 xhr.send();//提交請求 //等待伺服器返回內容 xhr.onreadystatechange = function() { if ( xhr.readyState == 4 ) { //如果內容響應成功,並解析完成 alert( xhr.responseText ); //彈出內容 } } } 複製程式碼 下面我們就講到 ,xhr.send();這一句呢才是真正請求資料的,open方法只是設定了一些請求引數。 現在呢請求遞交了,就等伺服器迴應了,這個時候Ajax的一個屬性就要登場了,那就是 responseText ,ajax請求返回的內容都放在了這裡面,而且不管你請求的內容是什麼,這裡存放的都是是字串型別。 當然我們上文也提到了,要想用非同步請求呢,這裡需要條件判斷才知道服務端對請求的內容是否響應完畢,這個時候另一個屬性就要登場了,readyState,他代表著Ajax請求過程的不同的狀態,引數如下: 0 (初始化)還沒有呼叫open()方法 1 (載入)已呼叫send()方法,正在傳送請求 2 (載入完成)send()方法完成,已收到全部響應內容 3 (解析)正在解析響應內容(因為收到的內容 並不是人能看懂的內容,所以需要解析) 4 (完成)響應內容解析完成,可以在客戶端呼叫了 由上我們可以得到,在狀態2的時候已經迴應了請求的內容了,但是這個內容我們人看不懂撒,機器才懂,所以就有3,幫我們解析這個內容,然後解析完成就變成4了,這個時候的內容,咱們前端就可以用了。 狀態是有了,可咱們怎麼能知道啥時候是啥狀態呢,這個時候我們就要用到一個監控狀態的事件了onreadystatechange事件,當狀態值改變的時候觸發會觸發這個事件,所以當狀態為4的時候我們再彈出內容。 上面的程式碼基本已經瞭解了原理,不過當然不是最完善的,這個時候,我們雖然監控到了狀態,響應了內容,但是內容不一定就是對的呀,比如可能內容出錯了,可能我們請求了一個不存在的頁面,這個時候readyState是無法判斷錯誤的,我們需要知道內容是否正常,這個時候另一個屬性 status屬性就登場了,它代表的不是Ajax狀態,而是伺服器(請求資源)的狀態, http狀態碼。狀態碼有很多,其中比較出名的就是200,成功狀態碼,和404 Not Found.其他的大傢俬下自行查閱。這裡可以看到。 所以我們的程式碼要做進一步的改進 複製程式碼 var oBtn = document.getElementById('btn'); oBtn.onclick = function(){ var xhr = null; if(window.XMLHttpRequest){ xhr = new XMLHttpRequest(); }else{ xhr = new ActiveXObject('Microsoft.XMLHTTP'); } xhr.open('get','1.txt',true); //設定請求資訊 xhr.send();//提交請求 //等待伺服器返回內容 xhr.onreadystatechange = function() { if ( xhr.readyState == 4 ) { if(xhr.status == 200) {//如果響應成功,並且伺服器相應內容正確 alert( xhr.responseText );//彈出內容 }else{alert('出錯了' + xhr.status); //否則告知出錯並彈出錯誤原因 } } } 複製程式碼 Ajax的大概流程就是這樣的。當然還存在一些細節方面的問題需要注意的,繼續往下看把。 GET請求: 1 可能存在快取問題:後臺更改了 因網址未變 所以會去快取提取內容 而不是後臺 來看個栗子:假如我們要點選按鈕彈出名字和年齡,因為GET請求是通過數值串連然後在網址傳遞資料的,所以我們的open方法可以直接這樣寫: xhr.open('get','1.get.php?username=沐晴&age=21',true); 後臺程式碼不變 複製程式碼 <?php header('content-type:text/html;charset="utf-8"'); //設定編碼格式,以及文件型別 error_reporting(0); $username = $_GET['username'];//獲取get請求方式的資料 $age = $_GET['age']; echo "你的名字:{$username},年齡:{$age}"; //輸出內容 複製程式碼 複製程式碼 現在點選肯定會彈出你的名字沐晴,年齡21 了。 這個時候呢,瀏覽器會有一個快取,如果下次訪問相同的網址,就會從快取裡取。 比如我現在想彈出,歡迎你,你的名字沐晴,年齡21, echo "歡迎,你的名字:{$username},年齡:{$age}"; //輸出內容 雖然後臺程式碼變了,但是GET請求訪問網址依然是 1.get.php?username=沐晴&age=21,所以後臺會去瀏覽器快取找,結果彈出的還是原來的。大家可以自行測試。 所以,這個時候我們需要解決快取問題。既然訪問網址不變的話會去找快取,那麼我們讓網址一直變不就好了。所以我們可以在後面加個一直變化的變數,比如系統事件,或者加一個隨機數都行,像下面這樣: xhr.open('get','1.get.php?username=沐晴&age=21&'+new.Date.getTime(),true); 這樣就不會存在快取問題了。有些人會這樣寫,會在後面給它起個名字t,這個時候如果後臺也有個變數叫t,可能就會出問題了,所以不是很推薦。 xhr.open('get','1.get.php?username=沐晴&age=21&t='+new.Date.getTime(),true); post 請求 1 Ajax中post 資料放在send裡面作為引數傳遞。 2 我們上次我們知道表單裡面的第三個引數:enctype: 提交的資料格式,預設是application/x-www-form-urlencoded,但是在Ajax中,你不寫就沒有,沒有預設值,所以我們需要在請求頭裡面指定提交的資料格式,不然瀏覽器不知道用哪種格式解析。 所以post請求需要設定下面這兩句 1 xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded'); 2 xhr.send('username=沐晴&age=21'); 3 無快取問題,因為單單是往伺服器提交資料,提交資料是不會被快取的,獲取資料才會被快取。這就是web的機制。 前面講的都是請求資料,現在來看一下後端接收請求,然後響應給我們的內容。 先看看後端對資料的處理:後端的資料型別也是很多的,我們不能直接把這樣的資料給前端吧,所以後端也需要做一定的處理,它有個方法 json_encode 可以根據資料型別不同,然後輸出不同格式。看下面的栗子 複製程式碼 <?php header('content-type:text/html;charset="utf-8"'); error_reporting(0); $arr1 = array('le','mo'); // 索引型別的資料 $arr2 = array('username'=>'le','age'=>32); // 2 對應關係的資料 echo json_encode($arr1); // ["le","mo"] 索引型別 輸出為陣列格式 echo json_encode($arr2); // {"username":"le","age":32} 對應關係的資料 輸出為json格式 複製程式碼 雖然後端輸出的內容格式上是陣列和json但是我之前提到過 alert( xhr.responseText );//這裡彈出的可都是字串型別,所以儘管格式上看著是json和陣列,但實際的資料型別還是字串。 所以我們前端要對這些字串進行轉換。這個時候就用到了兩個方法 1 stringify() : 把json物件轉化成字串 轉換後的字串是嚴格的json格式 2 parse() : 把字串轉成物件,可以把後端返回的字串 轉成JSON格式,對於json,只能轉換嚴格json格式的字串,字串的鍵 比較用雙引號括起來 像這樣 {"username":"le","age":32} 下面來看個實際的案例: 需求:點選頁面按鈕,實現頁面不重新整理,在下面顯示新聞列表 看註釋應該能看懂 複製程式碼 無標題文件
    複製程式碼 複製程式碼 <?php header('content-type:text/html;charset="utf-8"'); error_reporting(0); $news = array( array('title'=>'女總理默克爾滑雪時摔倒 骨盆斷裂','date'=>'2014-1-6'), array('title'=>'駐英外交官撰文互稱對方國家為"伏地魔"','date'=>'2014-1-6'), array('title'=>'安倍:望與中國領導人會面 中方:你關閉了大門','date'=>'2014-1-6'), array('title'=>'揭祕臺灣駐港間諜網運作 湖北宜昌副市長被查','date'=>'2014-1-6'), array('title'=>':嫦娥三號是貨真價實的中國創造','date'=>'2014-1-6'), ); echo json_encode($news); 複製程式碼