最近在作一個單頁應用,node和瀏覽器僅經過json傳輸數據,由於是隻有本身用等因而鍛鍊一下本身,因此也不用考慮seo的問題,node端我已經寫好了,可是瀏覽器端想要用ajax原生太麻煩,用封裝的函數又須要引入angular,jquery等大型框架。我寫node比較多,以爲用什麼功能就引入什麼功能,不太喜歡用大而全的框架,因此只好手動封裝一下ajax的操做node
ajax的xhr對象有7個事件jquery
onloadstart 開始send觸發ajax
onprogress 從服務器上下載數據每50ms觸發一次json
onload 獲得響應segmentfault
onerror 服務器異常promise
onloadend 請求結束,不管成功失敗瀏覽器
onreadystatechange xhr.readyState改變使觸發服務器
onabort 調用xhr.abort時觸發框架
關於ajax,我找到了一篇很好的文章,這裏就再也不贅述:https://segmentfault.com/a/1190000004322487異步
原本我是想註冊onload和onerror簡單封裝一下,後來發現即便後臺返回500也依舊會觸發onload事件,
因此最終個人選擇是註冊onloadend事件,經過判斷xhr.status來決定是引起resolve仍是reject,同時
對於xhr對象還註冊ontimeout,onabort,onerror函數,並在reject時返回一個對象,包含errorType和xhr,
返回xhr的緣由在於使用這個函數時可能使用xhr對象的一些其餘參數,resolve也是返回這個
代碼以下:
// ajax函數的默認參數 var ajaxOptions = { url: '#', method: 'GET', async: true, timeout: 0, data: null, dataType: 'text', headers: {}, onprogress: function () { }, onuploadprogress: function () { }, xhr: null } /** * ajax函數,返回一個promise對象 * @param {Object} optionsOverride 參數設置,支持的參數以下 * url: url地址,默認"#" * method: 請求方法,僅支持GET,POST,默認GET * async: 是否異步,默認true * timeout: 請求時限,超時將在promise中調用reject函數 * data: 發送的數據,該函數不支持處理數據,將會直接發送 * dataType: 接受的數據的類型,默認爲text * headers: 一個對象,包含請求頭信息 * onprogress: 處理onprogress的函數 * ouploadprogress: 處理.upload.onprogress的函數 * xhr: 容許在函數外部建立xhr對象傳入,但必須不能是使用過的 * @return {Promise} * 該函數註冊xhr.onloadend回調函數,判斷xhr.status是否屬於 [200,300)&&304 , * 若是屬於則promise引起resolve狀態,容許拿到xhr對象 * 若是不屬於,或已經引起了ontimeout,onabort,則引起reject狀態,容許拿到xhr對象 * * 關於reject * 返回一個對象,包含 * errorType:錯誤類型, * abort_error: xhr對象調用abort函數 * timeout_error: 請求超時 * onerror: xhr對象觸發了onerror事件 * send_error: 發送請求出現錯誤 * status_error: 響應狀態不屬於 [200,300)&&304 */ function ajax(optionsOverride) { // 將傳入的參數與默認設置合併 var options = {}; for (var k in ajaxOptions) { options[k] = optionsOverride[k] || ajaxOptions[k]; } options.async = options.async === false ? false : true; var xhr = options.xhr = options.xhr || new XMLHttpRequest(); return new Promise(function (resolve, reject) { xhr.open(options.method, options.url, options.async); xhr.timeout = options.timeout; //設置請求頭 for (var k in options.headers) { xhr.setRuquestHeader(k, options.headers[k]); } // 註冊xhr對象事件 xhr.onprogress = options.onprogress; xhr.upload.onprogress = options.onuploadprogress; xhr.responseType = options.dataType; xhr.onabort = function () { reject(new Error({ errorType: 'abort_error', xhr: xhr })); } xhr.ontimeout = function () { reject({ errorType: 'timeout_error', xhr: xhr }); } xhr.onerror = function () { reject({ errorType: 'onerror', xhr: xhr }) } xhr.onloadend = function () { if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) resolve(xhr); else reject({ errorType: 'status_error', xhr: xhr }) } try { xhr.send(options.data); } catch (e) { reject({ errorType: 'send_error', error: e }); } }) }
另一點,關於參數我沒有過多的檢查,好比method是否爲'GET'或'POST',一個是在於參數檢查很繁瑣(盡情說我懶把),另一個在於參數應該使用強制規範,在文檔中標註,使用這個函數出錯了你就應該本身負責,而不是我幫你檢查後給你修改過來,這也不利於調用者理解這個函數。
使用代碼以下:
ajax({ url: 'http://localhost:3000/suc', async: true, onprogress: function (evt) { console.log(evt.position/evt.total); }, dataType:'text/json' }) .then(function (xhr) { console.log(xhr.response.name); }, function (e) { console.log(JSON.stringify(e)) })