最近在做一個單頁應用,node和瀏覽器僅通過json傳輸數據,因為是只有自己用等於是鍛煉一下自己,所以也不用考慮seo的問題,node端我已經寫好了,但是瀏覽器端想要用ajax原生太麻煩,用封裝的函數又需要引入angular,jquery等大型框架。我寫node比較多,覺得用什么功能就引入什么功能,不太喜歡用大而全的框架,所以只好手動封裝一下ajax的操作
ajax的xhr對象有7個事件
onloadstart 開始send觸發
onprogress 從服務器上下載數據每50ms觸發一次
onload 得到響應
onerror 服務器異常
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)) })