不用庫(框架),本身寫ajax

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

  寫個ajax,一來能夠經歷一下處理問題的過程,提高技術能力,二來工做中有時真的用不着這麼大的一個庫(框架),用本身寫的,何樂不爲呢。php

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

$.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(){} 	//請求完成以後(不論成功或失敗)
});

   這樣的調用是否是很溫馨、方便,若是感受溫馨那本身動手寫也參照這種設計方式,不用太複雜,知足所需就好java

  解ajax的基礎知識ajax

  XMLHttpRequest 對象數據庫

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

  建立一個兼容的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差很少分析完了,根據實際須要,添加各類功能,去思考每種功能是怎樣實現的,並能找到解決方法。

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

相關文章
相關標籤/搜索