對於前端來說,請求是前端日常工作必備的,通過請求才能與后端進行數據交互,尤其在現在前后端分離的開發模式下,請求顯得就更加重要。因此,對於前端開發者來說,掌握請求就很重要。下面將從http請求和常見的幾個請求技術做具體的講解
一、XMLHttpRequest
XMLHttpRequest一開始只是微軟瀏覽器提供的一個接口,后來各大瀏覽器紛紛效仿也提供了這個接口,再后來W3C對它進行了標准化,按照標准前后可以分為兩個版本,具體闡述如下:
版本以(老版本):
//新建一個XMLHttpRequest對象 var xhr=new XMLHttpRequest(); //進行請求 xhr.open('GET', 'url'); xhr.send(); //等待服務器響應 xhr.onreadystatechange = function(){ //該函數會被調用四次,因此需要判斷狀態是否為4 if ( xhr.readyState == 4 && xhr.status == 200 ) { alert( xhr.responseText ); } else { alert( xhr.statusText ); } };
在老版本中的,對應的具體屬性說明如下:
- xhr.readyState:XMLHttpRequest對象的狀態,等於4表示數據已經接收完畢。
- xhr.status:服務器返回的狀態碼,等於200表示一切正常。
- xhr.responseText:服務器返回的文本數據
- xhr.responseXML:服務器返回的XML格式的數據
- xhr.statusText:服務器返回的狀態文本。
老版本因為不是統一的標准,各個瀏覽器廠商在實現的時候都有一定的差異,而且在存在一些缺陷:
- 只支持文本數據的傳送,無法用來讀取和上傳二進制文件。
- 傳送和接收數據時,沒有進度信息,只能提示有沒有完成。
- 受到"同域限制"(Same Origin Policy),只能向同一域名的服務器請求數據。
版本二(標准后的版本):
為了更好的使用XMLHttpRequest,w3school發布了標准版本,該版本彌補了版本一的缺陷,也被各大瀏覽器廠商接受並實現。具體為:
- 可以設置HTTP請求的時限。
- 可以使用FormData對象管理表單數據。
- 可以上傳文件。
- 可以請求不同域名下的數據(跨域請求)。
- 可以獲取服務器端的二進制數據。
- 可以獲得數據傳輸的進度信息。
當然,一般為了友好的進行兼容各個瀏覽器,會采用對瀏覽器進行判斷並進行兼容性模式來獲取XMLHttpRequest的對象
var xhr;
if (window.XMLHttpRequest) { // Mozilla, Safari... xhr = new XMLHttpRequest(); } else if (window.ActiveXObject) { // IE try { xhr = new ActiveXObject('Msxml2.XMLHTTP'); } catch (e) { try { xhr = new ActiveXObject('Microsoft.XMLHTTP'); //IE5,6 } catch (e) {} } } // 請求成功回調函數 xhr.onload = e => { console.log('request success'); }; // 請求結束 xhr.onloadend = e => { console.log('request loadend'); }; // 請求出錯 xhr.onerror = e => { console.log('request error'); }; // 請求超時 xhr.ontimeout = e => { console.log('request timeout'); }; // 請求回調函數.XMLHttpRequest標准又分為Level 1和Level 2,這是Level 1和的回調處理方式 // xhr.onreadystatechange = () => { // if (xhr.readyState !== 4) { // return; // } // const status = xhr.status; // if ((status >= 200 && status < 300) || status === 304) { // console.log('request success'); // } else { // console.log('request error'); // } // }; xhr.timeout = 0; // 設置超時時間,0表示永不超時 // 初始化請求 xhr.open('GET/POST/DELETE/...', '/url', true || false); // 設置期望的返回數據類型 'json' 'text' 'document' ... xhr.responseType = ''; // 設置請求頭 xhr.setRequestHeader('', ''); // 發送請求 xhr.send(null || new FormData || 'a=1&b=2' || 'json字符串');
二、ajax請求
AJAX 是一種與服務器交換數據的技術,可以在不重新載入整個頁面的情況下更新網頁的一部分,其實就是對XMLHttpRequest的封裝,可以直接引入jquery工具包來進行調用ajax請求(jquery是一個js工具包,其特點是:寫得少,做得多),具體的ajax常用方式如下:
| 方法 | 描述 |
|---|---|
| $.ajax() | 執行異步 AJAX 請求 |
| $.ajaxPrefilter() | 在每個請求發送之前且被 $.ajax() 處理之前,處理自定義 Ajax 選項或修改已存在選項 |
| $.ajaxSetup() | 為將來的 AJAX 請求設置默認值 |
| $.ajaxTransport() | 創建處理 Ajax 數據實際傳送的對象 |
| $.get() | 使用 AJAX 的 HTTP GET 請求從服務器加載數據 |
| $.getJSON() | 使用 HTTP GET 請求從服務器加載 JSON 編碼的數據 |
| $.getScript() | 使用 AJAX 的 HTTP GET 請求從服務器加載並執行 JavaScript |
| $.param() | 創建數組或對象的序列化表示形式(可用於 AJAX 請求的 URL 查詢字符串) |
| $.post() | 使用 AJAX 的 HTTP POST 請求從服務器加載數據 |
| ajaxComplete() | 規定 AJAX 請求完成時運行的函數 |
| ajaxError() | 規定 AJAX 請求失敗時運行的函數 |
| ajaxSend() | 規定 AJAX 請求發送之前運行的函數 |
| ajaxStart() | 規定第一個 AJAX 請求開始時運行的函數 |
| ajaxStop() | 規定所有的 AJAX 請求完成時運行的函數 |
| ajaxSuccess() | 規定 AJAX 請求成功完成時運行的函數 |
| load() | 從服務器加載數據,並把返回的數據放置到指定的元素中 |
| serialize() | 編碼表單元素集為字符串以便提交 |
| serializeArray() | 編碼表單元素集為 names 和 values 的數組 |
優點:
- 對原生XHR的封裝
- 針對MVC的編程
- 完美的兼容性
- 支持jsonp
缺點:
- 不符合MVVM
- 異步模型不夠現代,不支持鏈式,代碼可讀性差
- 整個Jquery太大,引入成本過高
當然,我們可以直接使用XMLHttpReqeust來進行實現自己的ajax封裝,具體代碼如下:
const http = {
/** * js封裝ajax請求 * >>使用new XMLHttpRequest 創建請求對象,所以不考慮低端IE瀏覽器(IE6及以下不支持XMLHttpRequest) * >>使用es6語法,如果需要在正式環境使用,則可以用babel轉換為es5語法 https://babeljs.cn/docs/setup/#installation * @param settings 請求參數模仿jQuery ajax * 調用該方法,data參數需要和請求頭Content-Type對應 * Content-Type data 描述 * application/x-www-form-urlencoded 'name=哈哈&age=12'或{name:'哈哈',age:12} 查詢字符串,用&分割 * application/json name=哈哈&age=12' json字符串 * multipart/form-data new FormData() FormData對象,當為FormData類型,不要手動設置Content-Type * 注意:請求參數如果包含日期類型.是否能請求成功需要后台接口配合 */ ajax: (settings = {}) => { // 初始化請求參數 let _s = Object.assign({ url: '', // string type: 'GET', // string 'GET' 'POST' 'DELETE' dataType: 'json', // string 期望的返回數據類型:'json' 'text' 'document' ... async: true, // boolean true:異步請求 false:同步請求 required data: null, // any 請求參數,data需要和請求頭Content-Type對應 headers: {}, // object 請求頭 timeout: 1000, // string 超時時間:0表示不設置超時 beforeSend: (xhr) => { }, success: (result, status, xhr) => { }, error: (xhr, status, error) => { }, complete: (xhr, status) => { } }, settings); // 參數驗證 if (!_s.url || !_s.type || !_s.dataType || !_s.async) { alert('參數有誤'); return; } // 創建XMLHttpRequest請求對象 let xhr = new XMLHttpRequest(); // 請求開始回調函數 xhr.addEventListener('loadstart', e => { _s.beforeSend(xhr); }); // 請求成功回調函數 xhr.addEventListener('load', e => { const status = xhr.status; if ((status >= 200 && status < 300) || status === 304) { let result; if (xhr.responseType === 'text') { result = xhr.responseText; } else if (xhr.responseType === 'document') { result = xhr.responseXML; } else { result = xhr.response; } // 注意:狀態碼200表示請求發送/接受成功,不表示業務處理成功 _s.success(result, status, xhr); } else { _s.error(xhr, status, e); } }); // 請求結束 xhr.addEventListener('loadend', e => { _s.complete(xhr, xhr.status); }); // 請求出錯 xhr.addEventListener('error', e => { _s.error(xhr, xhr.status, e); }); // 請求超時 xhr.addEventListener('timeout', e => { _s.error(xhr, 408, e); }); let useUrlParam = false; let sType = _s.type.toUpperCase(); // 如果是"簡單"請求,則把data參數組裝在url上 if (sType === 'GET' || sType === 'DELETE') { useUrlParam = true; _s.url += http.getUrlParam(_s.url, _s.data); } // 初始化請求 xhr.open(_s.type, _s.url, _s.async); // 設置期望的返回數據類型 xhr.responseType = _s.dataType; // 設置請求頭 for (const key of Object.keys(_s.headers)) { xhr.setRequestHeader(key, _s.headers[key]); } // 設置超時時間 if (_s.async && _s.timeout) { xhr.timeout = _s.timeout; } // 發送請求.如果是簡單請求,請求參數應為null.否則,請求參數類型需要和請求頭Content-Type對應 xhr.send(useUrlParam ? null : http.getQueryData(_s.data)); }, // 把參數data轉為url查詢參數 getUrlParam: (url, data) => { if (!data) { return ''; } let paramsStr = data instanceof Object ? http.getQueryString(data) : data; return (url.indexOf('?') !== -1) ? paramsStr : '?' + paramsStr; }, // 獲取ajax請求參數 getQueryData: (data) => { if (!data) { return null; } if (typeof data === 'string') { return data; } if (data instanceof FormData) { return data; } return http.getQueryString(data); }, // 把對象轉為查詢字符串 getQueryString: (data) => { let paramsArr = []; if (data instanceof Object) { Object.keys(data).forEach(key => { let val = data[key]; // todo 參數Date類型需要根據后台api酌情處理 if (val instanceof Date) { // val = dateFormat(val, 'yyyy-MM-dd hh:mm:ss'); } paramsArr.push(encodeURIComponent(key) + '=' + encodeURIComponent(val)); }); } return paramsArr.join('&'); } }
三、vue-resource請求
vue-resource是Vue.js的一款插件,它可以通過XMLHttpRequest或JSONP發起請求並處理響應。也就是說,$.ajax能做的事情,vue-resource插件一樣也能做到,而且vue-resource的API更為簡潔。另外,vue-resource還提供了非常有用的inteceptor功能,使用inteceptor可以在請求前和請求后附加一些行為,比如使用inteceptor在ajax請求時顯示loading界面。
特點
- 體積小
vue-resource非常小巧,在壓縮以后只有大約12KB,服務端啟用gzip壓縮后只有4.5KB大小,這遠比jQuery的體積要小得多。 - 支持主流的瀏覽器
和Vue.js一樣,vue-resource除了不支持IE 9以下的瀏覽器,其他主流的瀏覽器都支持。 - 支持Promise API和URI Templates
Promise是ES6的特性,Promise的中文含義為“先知”,Promise對象用於異步計算。
URI Templates表示URI模板,有些類似於ASP.NET MVC的路由模板。 - 支持攔截器
攔截器是全局的,攔截器可以在請求發送前和發送請求后做一些處理。
攔截器在一些場景下會非常有用,比如請求發送前在headers中設置access_token,或者在請求失敗時,提供共通的處理方式。
常用api
- get(url, [options])
- head(url, [options])
- delete(url, [options])
- jsonp(url, [options])
- post(url, [body], [options])
- put(url, [body], [options])
- patch(url, [body], [options])
| 客戶端請求方法 | 服務端處理方法 |
|---|---|
| this.$http.get(...) | Getxxx |
| this.$http.post(...) | Postxxx |
| this.$http.put(...) | Putxxx |
| this.$http.delete(...) | Deletexxx |
option詳解
| 參數 | 類型 | 描述 |
|---|---|---|
| url | string | 請求的URL |
| method | string | 請求的HTTP方法,例如:'GET', 'POST'或其他HTTP方法 |
| body | Object, FormData string | request body |
| params | Object | 請求的URL參數對象 |
| headers | Object | request header |
| timeout | number | 單位為毫秒的請求超時時間 (0 表示無超時時間) |
| before | function(request) | 請求發送前的處理函數,類似於jQuery的beforeSend函數 |
| progress | function(event) | ProgressEvent回調處理函數 |
| credentials | boolean | 表示跨域請求時是否需要使用憑證 |
| emulateHTTP | boolean | 發送PUT, PATCH, DELETE請求時以HTTP POST的方式發送,並設置請求頭的X-HTTP-Method-Override |
| emulateJSON | boolean | 將request body以application/x-www-form-urlencoded content type發送 |
四、fetch
- fetch是基於promise實現的,也可以結合async/await
- fetch請求默認是不帶cookie的,需要設置fetch(URL,{credentials:’include’})。
- Credentials有三種參數:same-origin,include,*
- 服務器返回400 500 狀態碼時並不會reject,只有網絡出錯導致請求不能完成時,fetch才會被reject
- 所有版本的 IE 均不支持原生 Fetch
- fetch是widow的一個方法
fetch(url).then(function(response) { return response.json(); }).then(function(data) { console.log(data); }).catch(function(e) { console.log("Oops, error"); });
可配合es6的箭頭函數進行使用
fetch(url).then(response => response.json()) .then(data => console.log(data)) .catch(e => console.log("Oops, error", e))
電腦刺綉綉花廠 http://www.szhdn.com 廣州品牌設計公司https://www.houdianzi.com
五、axios
Axios 是一個基於 promise 的 HTTP 庫,可以用在瀏覽器和 node.js 中
特點
- 從瀏覽器中創建XMLHttpRequests
- 從 node.js 創建http請求
- 支持PromiseAPI
- 攔截請求和響應
- 轉換請求數據和響應數據
- 取消請求
- 自動轉換 JSON 數據
- 客戶端支持防御XSRF
常用api
- axios.request(config)
- axios.get(url[, config])
- axios.delete(url[, config])
- axios.head(url[, config])
- axios.options(url[, config])
- axios.post(url[, data[, config]])
- axios.put(url[, data[, config]])
- axios.patch(url[, data[, config]]
實例
get請求
// 為給定 ID 的 user 創建請求 axios.get('/user?ID=12345') .then(function (response) { console.log(response); }) .catch(function (error) { console.log(error); }); // 可選地,上面的請求可以這樣做 axios.get('/user', { params: { ID: 12345 } }) .then(function (response) { console.log(response); }) .catch(function (error) { console.log(error); });
post請求
axios.post('/user', { firstName: 'Fred', lastName: 'Flintstone' }) .then(function (response) { console.log(response); }) .catch(function (error) { console.log(error); });
並發請求
function getUserAccount() { return axios.get('/user/12345'); } function getUserPermissions() { return axios.get('/user/12345/permissions'); } axios.all([getUserAccount(), getUserPermissions()]) .then(axios.spread(function (acct, perms) { // 兩個請求現在都執行完成 }));
攔截器
// 添加請求攔截器 axios.interceptors.request.use(function (config) { // 在發送請求之前做些什么 return config; }, function (error) { // 對請求錯誤做些什么 return Promise.reject(error); }); // 添加響應攔截器 axios.interceptors.response.use(function (response) { // 對響應數據做點什么 return response; }, function (error) { // 對響應錯誤做點什么 return Promise.reject(error); });
取消請求
var CancelToken = axios.CancelToken; var source = CancelToken.source(); axios.get('/user/12345', { cancelToken: source.token }).catch(function(thrown) { if (axios.isCancel(thrown)) { console.log('Request canceled', thrown.message); } else { // 處理錯誤 } }); // 取消請求(message 參數是可選的) source.cancel('Operation canceled by the user.');
