不用庫(框架),自己寫ajax


  平常會使用ajax來請求數據,加載一個庫(框架),或許僅僅maybe就使用了它的ajax部分。

  寫個ajax,一來可以經歷一下處理問題的過程,提升技術能力,二來工作中有時真的用不着這么大的一個庫(框架),用自己寫的,何樂不為呢。

  先來看看流行的jQuery是怎樣調用ajax的

$.ajax({
	url: 		'test.php', 	//發送請求的URL字符串
	type: 		'GET',			//發送方式 
	dataType: 	'json',			//預期服務器返回的數據類型 xml, html, text, json, jsonp, script
	data: 		'k=v&k=v',		//發送的數據 
	async:		true,			//異步請求 
	cache: 		false, 			//緩存 
	timeout: 	5000,			//超時時間 毫秒
	beforeSend: function(){},	//請求之前
	error: 		function(){},	//請求出錯時
	success: 	function(){},	//請求成功時
	complete: 	function(){} 	//請求完成之后(不論成功或失敗)
});

   這樣的調用是不是很舒適、方便,如果感覺舒適那自己動手寫也參照這種設計方式,不用太復雜,滿足所需就好

  解ajax的基礎知識

  XMLHttpRequest 對象

  XMLHttpRequest對象是ajax的核心,通過XMLHttpRequest對象來向服務器發異步請求,從服務器獲得數據,所有現代瀏覽器(IE7+、Firefox、Chrome、Safari、Opera)均支持 XMLHttpRequest 對象(IE5 和 IE6 使用 ActiveXObject)。  

  創建一個兼容的XMLHttpRequest對象

var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP'); 

   向服務器發送請求

xhr.open(method,url,async);
	//method:請求的類型;GET 或 POST
	//url:請求的URL
	//async:true(異步)或 false(同步)
xhr.send(string);
	//將請求發送到服務器
	//string:僅用於 POST 請求

//GET 比 POST 請求方式更簡單也更快,並且在大部分情況下都能用
//在以下情況中,請使用 POST 請求:
	//無法使用緩存文件(更新服務器上的文件或數據庫)
	//向服務器發送大量數據(POST 沒有數據量限制)
	//發送包含未知字符的用戶輸入時,POST 比 GET 更穩定也更可靠

  服務器響應

  使用 XMLHttpRequest 對象的 responseTextresponseXML 屬性獲得來自服務器的響應。

    如果來自服務器的響應是 XML,而且需要作為 XML 對象進行解析,請使用 responseXML 屬性。

    如果來自服務器的響應並非 XML,請使用 responseText 屬性,responseText 屬性返回字符串形式的響應。

  onreadystatechange 事件

  當請求被發送到服務器時,我們需要執行一些基於響應的任務。每當 readyState 改變時,就會觸發 onreadystatechange 事件。readyState 屬性存有 XMLHttpRequest 的狀態信息。

   XMLHttpRequest 對象的三個重要的屬性:

    onreadystatechange  //存儲函數(或函數名),每當 readyState 屬性改變時,就會調用該函數

    readyState  //存有 XMLHttpRequest 的狀態, 從 0 到 4 發生變化     

    • 0: 請求未初始化
    • 1: 服務器連接已建立
    • 2: 請求已接收
    • 3: 請求處理中
    • 4: 請求已完成,且響應已就緒

    status  //200: "OK", 404: 未找到頁面

  在 onreadystatechange 事件中,我們規定當服務器響應已做好被處理的准備時所執行的任務, 當 readyState等於4 且 status為200 時,表示響應已就緒

xhr.onreadystatechange = function(){
    if( xhr.readyState == 4 && xhr.status == 200 ){
		//准備就緒 可以處理返回的 xhr.responseText 或者 xhr.responseXML 
	}
};

   一個簡單的ajax請求如下:

var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');
xhr.onreadystatechange = function(){
    if( xhr.readyState == 4 && xhr.status == 200 ){
		//准備就緒 可以處理返回的 xhr.responseText 或者 xhr.responseXML 
	}
};
xhr.open(method,url,async);
xhr.send(string);

   補充:1. 發送GET請求時可能得到的是緩存的結果,為了避免這種情況,可以向URL 添加一個唯一的 ID,時間戳。2. 如果需要像HTML表單那樣 POST 數據,使用 setRequestHeader() 來添加 HTTP 頭。然后在 send() 方法中發送數據。

url += (url.indexOf('?') < 0 ? '?' : '&') + '_='+ (+new Date());
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');

  開始寫自己的ajax

  先寫一個基本的,定義好各種參數選項,供參考

var $ = (function(){
	//輔助函數 序列化參數 
	function param(data){
	    //..	
	}

	function ajax(opts){
		var _opts = {
			url 	   :  '/',  			//發送請求URL地址
			type       :  'GET', 			//發送請求的方式 GET(默認), POST
			dataType   :  '',				//預期服務器返回的數據類型 xml, html, text, json, jsonp, script
			data 	   :  null,				//發送的數據 'key=value&key=value', {key:value,key:value}   
			async	   :  true,				//異步請求 ture(默認異步), false
			cache	   :  true, 			//緩存 ture(默認緩存), false 
			timeout    :  5, 				//超時時間 默認5秒
			load 	   :  function(){}, 	//請求加載中
			error      :  function(){},		//請求出錯時
			success    :  function(){},		//請求成功時
			complete   :  function(){}		//請求完成之后(不論成功或失敗)
		}, aborted = false, key,
		xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');
		for(key in opts) _opts[key] = opts[key];				
		
		/*
		if(_opts.dataType.toLowerCase() === 'script'){
			//..
		}
		if(_opts.dataType.toLowerCase() === 'jsonp'){
			//..
		}
		*/
		if(_opts.type.toUpperCase() === 'GET'){
	    	if(param(_opts.data) !== ''){
	       		_opts.url += (_opts.url.indexOf('?') < 0 ? '?' : '&') + param(_opts.data);
	        }
	        !_opts.cache && ( _opts.url += (_opts.url.indexOf('?') < 0 ? '?' : '&') + '_='+(+new Date()) );
	    }

	    function checkTimeout(){
			if(xhr.readyState !== 4){
				aborted = true;
				xhr.abort();
		  	}
		}
		setTimeout(checkTimeout, _opts.timeout*1000);
		
		xhr.onreadystatechange = function(){
	    	if( xhr.readyState !== 4 ) _opts.load && _opts.load(xhr);
		    if( xhr.readyState === 4 ){
		    	var s = xhr.status, xhrdata;
				if( !aborted && ((s >= 200 && s < 300) || s === 304) ){
					switch(_opts.dataType.toLowerCase()){
			    		case 'xml':
			    			xhrdata = xhr.responseXML;
			    		break;
			    		case 'json':
			    			xhrdata = window.JSON && window.JSON.parse ? JSON.parse(xhr.responseText) : eval('(' + xhr.responseText + ')');
			    		break;
			    		default:
			    			xhrdata = xhr.responseText;
			    	}
					_opts.success && _opts.success(xhrdata,xhr);
				}else{
					_opts.error && _opts.error(xhr);
				}
				_opts.complete && _opts.complete(xhr);
	    	}
	    };		
	    xhr.open(_opts.type,_opts.url,_opts.async);
	    if(_opts.type.toUpperCase() === 'POST'){
	        xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
	    }
	    xhr.send(_opts.type.toUpperCase() === 'GET' ? null : param(_opts.data));
	}
	return {
		ajax: ajax
	}
})();

   定義好了參數選項,來分析一下。其中 dataType 是整個ajax的重點,代碼的簡單或者復雜都在它了。

  在這里dataType為預期服務器返回的數據類型:xml, html, text, json, jsonp, script

  1. 為xml時,來自服務器的響應是XML,使用 responseXML 屬性獲取返回的數據

  2. 為html、text、json時,使用 responseText 屬性獲取返回的數據

      a. 為html時,返回純文本HTML信息,其中包含的script標簽是否要在插入dom時執行 ( 代碼復雜度+3 )

      b. 為json時,  返回JSON數據,要安全、要便捷、要兼容  ( 代碼復雜度+2 )

  3. 為jsonp時,一般跨域才用它,不用原來的ajax請求了,用創建script法( 代碼復雜度+2 )

  4. 為script時:  要跨域時,不用原來的ajax請求了,用創建script法( 代碼復雜度+1 ); 不跨域,返回純文本JavaScript代碼, 使用 responseText 屬性獲取返回的數據 ( 代碼復雜度+1 )

  其中,在html片段中的script標簽、jsonp、script,都要用到創建script標簽的方式。

  處理dataType為json

xhrdata = window.JSON && window.JSON.parse ? JSON.parse(xhr.responseText) : eval('(' + xhr.responseText + ')');

  這是最簡單的處理方式了,要JSON兼容,可以用json2.js。

  處理dataType為jsonp

  jsonp是要通過script標簽來請求跨域的,先了解下流程:

     

  這上圖中 a.html中請求了 http://www.b.com/b.php?callback=add  (在ajax程序中請求url就是這個鏈接),在b.php中讀取了傳過來的參數 callback=add  根據獲取到的參數值(值為add),以JS語法生成了函數名,並把json數據作為參數傳入了這個函數,返回以JS語法生成的文檔給a.html,a.html解析並執行返回的JS文檔,調用了定義好的add函數。

   在程序中一般采用更通用的方式去調用,比如下面這個廣泛使用的loadJS函數:

function loadJS(url, callback) {
	var doc = document, script = doc.createElement('script'), body = doc.getElementsByTagName('body')[0];
	script.type = 'text/javascript';
	if (script.readyState) {  
		script.onreadystatechange = function() {
			if (script.readyState == 'loaded' || script.readyState == 'complete') {
				script.onreadystatechange = null;
				callback && callback();
			}
		};
	} else {  
		script.onload = function() {
			callback && callback();
		};
	}
	script.src = url;
	body.appendChild(script);
}

  這樣把請求的url,傳入loadJS函數,得到一樣的結果。

loadJS('http://www.b.com/b.php?callback=add');

  因為是動態創建script,請求成功返回,JS代碼就立即執行,如果請求失敗是沒有任何提示的。因此自定義的參數選項: _opts.success 能調用,_opts.error不能調用。

  ajax處理jsonp也有兩種情況:

  1. 設置了請求URL后的參數 callback=add 特別是定義了函數名add,請求成功返回,JS代碼就立即執行(這里就是調用 add({"a":8,"b":2})  )

  2. 在_opts.success中處理JSON數據,就是請求成功返回,JS代碼不執行,並把函數中的參數挪出來,作為_opts.success的參數返回( 這里相當於處理字符串 'add({"a":8,"b":2})' ,去掉 'add(' 和 ‘)’,得到 {"a":8,"b":2} )

  處理dataType為html

   如果不處理HTML片段中script標簽,直接把responseText返回值插入DOM樹就可以了。如果要處理script,就要把HTML片段中的script標簽找出來,對買個script單獨處理,並注意是script標簽中包含的JS代碼還是通過src請求的。

  處理dataType為script

  如果要跨域時,用創建script的方式,和處理jsonp類似; 不跨域,使用 responseText 屬性獲取返回的數據,可以用 eval 來讓代碼執行,也可以創建script來執行。

function addJS(text) {
	var doc = document, script = doc.createElement('script'), head = doc.getElementsByTagName('body')[0];
	script.type = 'text/javascript';
	script.text = text;
	body.appendChild(script);
}

  到此ajax差不多分析完了,根據實際需要,添加各種功能,去思考每種功能是怎樣實現的,並能找到解決方法。

    如果都是用現成庫(框架),技術談何進步呢?


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM