日常會使用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 對象的 responseText 或 responseXML 屬性得到來自服務器的響應。
若是來自服務器的響應是 XML,並且須要做爲 XML 對象進行解析,請使用 responseXML 屬性。
若是來自服務器的響應並不是 XML,請使用 responseText 屬性,responseText 屬性返回字符串形式的響應。
onreadystatechange 事件
當請求被髮送到服務器時,咱們須要執行一些基於響應的任務。每當 readyState 改變時,就會觸發 onreadystatechange 事件。readyState 屬性存有 XMLHttpRequest 的狀態信息。
XMLHttpRequest 對象的三個重要的屬性:
onreadystatechange //存儲函數(或函數名),每當 readyState 屬性改變時,就會調用該函數
readyState //存有 XMLHttpRequest 的狀態, 從 0 到 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差很少分析完了,根據實際須要,添加各類功能,去思考每種功能是怎樣實現的,並能找到解決方法。
若是都是用現成庫(框架),技術談何進步呢?