AJAX
XHR 對象用法
1 var xhr = new XMLRequestHttp() // 通過XMLHttpRequest 構造函數來創建
open 方法
xhr.open(method, url, async, user, password);
method:要發送的請求類型 GET、POST、PUT、DELETE 。(必選)
url:請求的URL (必選)
axync :布爾值,是否異步發送請求,默認true(true 時,已完成事務的通知可供事件監聽使用,如果 xhr.multipart為true,則此參數必須為true;false 時,send()方法直到收到答復前不會返回)
user:用戶名用於認證用途 默認 null
password:用戶名用於認證用途 默認 null
調用open方法不會真正發送請求,只是准備發送請求,並且URL有同源策略的限制(須和頁面的主域、端口、協議一致,只要一處不符合要求將報錯,數據將被攔截,可通過前后端配置,或使用代理來解決)。
setRequestHeader()
如需設置 Accpet 頭部信息,可通過setRequestHeader() 方法來設置
Accpet 頭部信息:告知客戶端可以處理的內容類型,用 MIME類型 表示;使用 Content-Type 服務端使用 `Content-Type` 通知客戶端它的選擇
媒體類型( MIME類型 ) :一種標准,用來表示文檔、文件或字節流的性質和格式。 完整類型列表
Content-Type :實體頭部用於指示資源的 MIME 類型,告訴客戶端實際返回的內容類型;瀏覽器會在某些情況下進行MIME查找,並不一定遵循此標題的值; 為了防止這種行為,可以將標題 X-Content-Type-Options 設置為 nosniff。
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
send 方法
xhr.send(data);
data:作為請求主體發送的數據,如果不需要通過請求主體發送數據,則必須傳 null
調用 send()發送請求,在收到響應后,響應的數據會自動填充XHR對象的屬性
responseText :從服務端返回的文本
1 xhr.onload = function () { 2 if (xhr.readyState === xhr.DONE) { 3 if (xhr.status === 200) {5 console.log(xhr.responseText); 6 } 7 } 8 };
responseXML
如果響應的 Content-Type 為 text/html 或 application/xml,將保存包含響應數據的 XML DOM 文檔,對於其它類型的數據則為 null, 也可通過overrideMimeType() 強制 XHR 對象解析為 XML
1 // overrideMimeType() 用來強制解析 response 為 XML 2 xhr.overrideMimeType('text/xml'); 3 4 xhr.onload = function () { 5 if (xhr.readyState === xhr.DONE) { 6 if (xhr.status === 200) { 8 console.log(xhr.responseXML); 9 } 10 } 11 };
status
返回響應的HTTP狀態碼,請求完成前值為0,如果XHR 對象出錯 值也是0, 200 表示請求成功,304表示請求的資源並沒有修改,可直接使用瀏覽器種緩存的數據。 其它狀態信息
statusText
返回響應的HTTP狀態說明,status 值為 200 時 statusText為 "OK"
readyState
返回一個當前XHR對象所處的活動狀態
| 值 | 狀態 | 描述 |
|---|---|---|
0 |
UNSENT |
代理被創建,但尚未調用 open() 方法。 |
1 |
OPENED |
open() 方法已經被調用。 |
2 |
HEADERS_RECEIVED |
send() 方法已經被調用,並且頭部和狀態已經可獲得。 |
3 |
LOADING |
下載中;響應體部分正在被接收 responseText 屬性已經包含部分數據。 |
4 |
DONE |
下載操作已完成。 |
onreadystatechange
當 readyState變化時會觸發次事件函數,如果使用 abort() 取消請求則次事件函數不會被觸發
1 xhr.onreadystatechange = function () { 2 if(xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) { 3 console.log(xhr.responseText) 4 } 5 }
參考資料
兼容性

封裝 XMLHttpRequest 對象
1 // 創建 構造函數 2 function Ajax(obj) { 3 this.url = obj.url ||''; 4 this.type = obj.type || 'get'; 5 this.data = obj.data ||{}; 6 this.success = obj.success || null; 7 this.error = obj.error || null; 8 } 9 // 原型上創建方法支持 post 和 get 10 Ajax.prototype.send = function(){ 11 var self = this; 12 var toStr = Object.prototype.toString; 13 if (self.data === null && typeof self.data !== 'object' && Array.isArray(obj)) return; 14 return (function(){ 15 // 實例化 XML對象 16 var xhr = new XMLHttpRequest(); 17 var data = ''; 18 // 序列化參數 19 for (var k in self.data){ 20 data += k + '=' + self.data[k] + '&'; 21 } 22 data = data.substr(0,data.length - 1); 23 // 接收回調函數 24 xhr.onreadystatechange = function(){ 25 if (xhr.readyState === 4){ 26 if(xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) { 27 isFunction(self.success) && self.success(xhr.responseText) 28 }else{ 29 isFunction(self.error) && self.error(xhr) 30 } 31 } 32 } 33 // 初始化請求 34 if(self.type.toLocaleLowerCase() === 'post'){ 35 xhr.open ('post',self.url,true) 36 // 設置請求頭 37 xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded'); 38 //發送請求 39 xhr.send(data) 40 } else { 41 xhr.open('get', self.url + "?" + data,true) 42 xhr.send(null) 43 } 44 }()); 45 }; 46 47 function isFunction(obj){ 48 return toStr.call(obj) === "[object Function]" 49 } 50 51 var ajax = new Ajax({ 52 type:'post', 53 url:"/login", 54 data:{ 55 loginname:"admin", 56 password:"admin" 57 }, 58 success:function(e){ 59 console.log(e) 60 }, 61 error:function(err){ 62 console.log(err) 63 }, 64 }).send();
XMLHttpRequest Level 2 相比於 老版本的 XMLHttpRequest 新增以下內容:
可以設置 HTTP 請求超時時間
1 var xhr = XMLHttpRequest(); 2 xhr.open('GET'.'url'); 3 // 超時 2s 4 xhr.timeout = 2000; 5 // 超時處理 6 xhr.ontimeout = function(e) { 7 console.log(e) 8 } 9 xhr.send(null)
可以通過 FormData 發送表單數據
1 // 實例化 FormData 2 var formData = new FormData(); 3 // 添加數據 4 formData.append(key,value); 5 6 xhr.open('POST','url'); 7 xhr.send(formData);
可以上傳文件
FormData除了可以添加字符串數據,也可以添加blob、file類型的數據,因此可以用於上傳文件。- 在瀏覽器中,一般是通過文件上傳輸入框來獲取 file 對象,比如:
1 <input type="file" name='uploadFile' id="upload-file" />
1 document.getElementById('upload-file') 2 .addEventListener('change', function () { 3 4 var formData = new FormData(); 5 // 獲取數據 6 formData.append('uploadFile', this.files[0]) 7 xhr.send(formData) 8 })
支持跨域請求
- 瀏覽器默認是不允許跨域請求的,有時候又是必要的,在以前通常使用
JSONP來解決(IE10 以下不支持) - 為了標准化跨域請求, W3C提出 跨域資源共享(CORS)前端無須修改代碼,只需 服務器返回
Access-Control-Allow-Origin響應頭,指定允許對應的域 CORS默認不發送cookie如果需要發送,前端需要設置withCredentials屬性,同時服務器需要 返回Access-Control-Allow-Credentials: true,xhr.withCredentials = true;
1 let createCORSRequest = (method,url)=>{ 2 let var xhr = mew XMLHttpRequest(); 3 if ('withCredentials' in xhr){ 4 xhr.open(method,url,true); 5 }else if(typeof XDomainRequest != 'undefined'){ 6 xhr = new XDomainRequest(); 7 xhr.open(method,url); 8 }else{ 9 xhr = null 10 } 11 return xhr 12 } 13 let request = createCORSRequest('get','baidu.com') 14 if(request){ 15 request.onload = function(){ 16 // request.responseText 17 } 18 request.send() 19 }
Preflighted Requests:
img src特性:
1 let img = new Image(); 2 img.onload = function (){ 3 console.log('done') 4 } 5 img.src = 'http://www.baidu.com?test=test1'
可以獲取服務端二進制數據
1. 使用 overrideMimeType 方法覆寫服務器指定的 MIME 類型,從而改變瀏覽器解析數據的方式
1 // 參數 MIME 類型 2 // 告訴瀏覽器,服務器響應的內容是用戶自定義的字符集 3 xhr.overrideMimeType('text/plain; charset=x-user-defined'); 4 // 瀏覽器就會將服務器返回的二進制數據當成文本處理,我們需要做進一步的轉換才能拿到真實的數據 5 // 獲取二進制數據的第 i 位的值 6 var byte = xhr.responseText.charCodeAt(i) & 0xff
"& 0xff" 運算 參考 阮一峰的文章
2.xhr.responseType 用於設置服務器返回的數據的類型,將返回類型設置為 blob 或者 arraybuffer,然后就可以從 xhr.response 屬性獲取到對應類型的服務器返回數據。
1 xhr.responseType = 'arraybuffer' 2 xhr.onload = function () { 3 var arrayBuffer = xhr.response 4 // 接下來對 arrayBuffer 做進一步處理... 5 }
可以獲取數據傳輸進度信息 參考資料
使用 onload 監聽了一個數據傳輸完成的事件。
- 基於 Promise 的 Http 庫
- 可以在客戶端 和 nodeJs中使用
- 在客戶端創基 XMLHttpRequests
- 在nodeJs 創建 HTTP 請求
- 支持Promise
- 可攔截轉化請求和響應數據
- 取消請求
- 自動轉化JSON數據
- 支持客戶端 XSRF
兼容性

安裝
1 npm install axios
methods
Get
1 const axios = require('axios') 2 3 axios.get('url?id=xxx') 4 .then(res => { 5 console.log(res) 6 }) 7 .catch(err =>{ 8 console.log(err) 9 }) 10 //or 11 axios.get('url',{ 12 params:{ 13 id:'xxxxx' 14 } 15 }) 16 .then(res =>{ 17 console.log(res) 18 }) 19 .catch(err =>{ 20 console.log(err) 21 })
同樣的傳參方法有 delete
post
axios.post('url',{name:'Owen'})
.then(res =>{
console.log(res)
})
.catch(err =>{
console.log(err)
})
同樣的傳參方法有 put patch
concurrent requests
1 axios.all([axios.get('url1'),axios.get('url2')])
API
axios(config)
1 axios({ 2 method:'get', // default is get 3 url:'url', // request url 4 data:{ // 僅支持post,put和patch方法,數據作為請求主體發送 ( Only the post,put and patch methods are supported, and the data is sent as the request body ) 5 /* 瀏覽器僅支持傳遞 FormData, File, Blob (The browser only supports passing FormData, File and Blob) 6 Node 僅支持傳遞 Stream, Buffer (The Node only supports passing Stream, Buffer) 7 */ 8 name:'owen' 9 }, 10 baseURL:'base/url', // 除非url是絕對路徑,否則將baseURL添加到url的前面 (Add baseURL to then front of the url unless the url is an absolute path) 11 transformRequest: [function (data, headers) { 12 // 可以修改發送的請求數據和請求頭,只支持put,post和patch,回調函數必須返回Buffer,ArrayBuffer,FormData或Stream數據 13 // Can modify the sent request data and request header,only support put, post and patch. 14 // Callback must return Buffer, ArrayBuffer, FormData or Stream data 15 16 // Do whatever you want to transform the data 17 18 return data; 19 }], 20 transformResponse: [function (data) { 21 // 修改響應數據,再傳遞給 then或catch 方法 (Modify the response data and pass it to the then or catch method) 22 // Do whatever you want to transform the data 23 24 return data; 25 }], 26 headers: {'X-Requested-With': 'XMLHttpRequest'}, // 自定義請求頭 (Custom request header) 27 params:{ // 添加到url尾部的參數,一般用於get 和 delete( Parameters addde to the end of the url,generally used for get and delete ) 28 id:'xxx' 29 }, 30 paramsSerializer: function (params) { //序列化 [params] (https://www.npmjs.com/package/qs) 31 return Qs.stringify(params, {arrayFormat: 'brackets'}) 32 }, 33 timeout:1000,// default is 0 , 設置請求超時時間,單位毫秒 ( Set request timeout in milliseconds ) 34 withCredentials: true, // default is false, 跨域時是否攜帶cookie( Whether to carry cookies when crossing domains ) 35 adapter: function (config) { 36 /*攔截響應數據*/ 37 // At this point: 38 // - config has been merged with defaults 39 // - request transformers have already run 40 // - request interceptors have already run 41 42 // Make the request using config provided 43 // Upon response settle the Promise 44 return new Promise(function(resolve, reject) { 45 46 var response = { 47 data: responseData, 48 status: request.status, 49 statusText: request.statusText, 50 headers: responseHeaders, 51 config: config, 52 request: request 53 }; 54 55 settle(resolve, reject, response); 56 57 // From here: 58 // - response transformers will run 59 // - response interceptors will run 60 61 /** 62 * Resolve or reject a Promise based on response status. 63 * 64 * @param {Function} resolve A function that resolves the promise. 65 * @param {Function} reject A function that rejects the promise. 66 * @param {object} response The response. 67 */ 68 function settle(resolve, reject, response) { 69 var validateStatus = response.config.validateStatus; 70 if (!validateStatus || validateStatus(response.status)) { 71 resolve(response); 72 } else { 73 reject(createError( 74 'Request failed with status code ' + response.status, 75 response.config, 76 null, 77 response.request, 78 response 79 )); 80 } 81 }; 82 /** 83 * Create an Error with the specified message, config, error code, request and response. 84 * 85 * @param {string} message The error message. 86 * @param {Object} config The config. 87 * @param {string} [code] The error code (for example, 'ECONNABORTED'). 88 * @param {Object} [request] The request. 89 * @param {Object} [response] The response. 90 * @returns {Error} The created error. 91 */ 92 function createError(message, config, code, request, response) { 93 var error = new Error(message); 94 return enhanceError(error, config, code, request, response); 95 } 96 97 /** 98 * Update an Error with the specified config, error code, and response. 99 * 100 * @param {Error} error The error to update. 101 * @param {Object} config The config. 102 * @param {string} [code] The error code (for example, 'ECONNABORTED'). 103 * @param {Object} [request] The request. 104 * @param {Object} [response] The response. 105 * @returns {Error} The error. 106 */ 107 function enhanceError(error, config, code, request, response) { 108 error.config = config; 109 if (code) { 110 error.code = code; 111 } 112 113 error.request = request; 114 error.response = response; 115 error.isAxiosError = true; 116 117 error.toJSON = function() { 118 return { 119 // Standard 120 message: this.message, 121 name: this.name, 122 // Microsoft 123 description: this.description, 124 number: this.number, 125 // Mozilla 126 fileName: this.fileName, 127 lineNumber: this.lineNumber, 128 columnNumber: this.columnNumber, 129 stack: this.stack, 130 // Axios 131 config: this.config, 132 code: this.code 133 }; 134 }; 135 return error; 136 } 137 }); 138 }, 139 auth:{ // 表示應使用HTTP Basic身份驗證,並提供憑據 ( indicates that HTTP Basic auth should be used, and supplies credentials. ) 140 user:'xxx', 141 password:'***' 142 }, 143 responseType: 'json',/* 服務器響應的數據類型( The server response data type ) 144 支持 arraybuffer, blob, document, json, text, stream 145 */ 146 responseEncoding:'utf8', // 用於解碼響應的編碼 (Encoding for decoding the response ) 147 xsrfCookieName: 'XSRF-TOKEN', // default is XSRF-TOKEN , csrf令牌Cookie 名稱 148 xsrfHeaderName: 'X-XSRF-TOKEN', //default is X-XSRF-TOKEN, xsrf標記值的http標頭的名稱 149 onUploadProgress: function (progressEvent) { //上傳進度事件 (handling of progress events for uploads ) 150 console.log(progressEvent) 151 }, 152 onDownloadProgress: function (progressEvent) { // 下載進度事件 ( handling of progress events for downloads) 153 console.log(progressEvent) 154 }, 155 maxContentLength: 2000, // 允許響應內容的最大字節 (defines the max size of the http response content in bytes allowed) 156 validateStatus: function (status) { // 返回給定HTTP狀態范圍, 如果狀態在給定范圍內,響應數據傳給`then` ,否則傳給 `catch` ( Returns the given HTTP status range, if the status is within the give range, the respones data is passed to `then`, otherwise passed to `catch` ) 157 return status >= 200 && status < 300; // default 158 }, 159 maxRedirects: 5, // default is 5 // 定義Node 中最大重定向數 ( defines the maximunn number of redirects in Node ) 160 socketPath: null, // default is null 定義要在node.js中使用的 UNIX socket 161 httpAgent: new http.Agent({ keepAlive: true }), // node 中 http 和 https 的代理 162 httpsAgent: new https.Agent({ keepAlive: true }),// http://nodejs.cn/api/http.html 163 proxy: { // 代理配置 164 host: '127.0.0.1', 165 port: 9000, 166 auth: { 167 username: 'mikeymike', 168 password: 'rapunz3l' 169 } 170 }, 171 cancelToken: new CancelToken(function (cancel) { // 取消請求的 token 172 }) 173 }) 174 .then(res =>{ 175 console.log(res) 176 }) 177 .catch(err =>{ 178 console.log(err) 179 })
全局配置
通過 axios.create 方法來替換全局配置
const instance = axios.create({ baseURL: 'base/url' });
通過axios.defaults 對象替換全局默認配置
1 instance.defaults.headers.common['Authorization'] = AUTH_TOKEN; 2 instance.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
攔截器
攔截請求數據
1 axios.interceptors.request.use(function (config) { 2 return config; 3 }, function (error) { 4 return Promise.reject(error); 5 });
攔截響應數據
1 axios.interceptors.response.use(function (response) { 2 // Do something with response data 3 return response; 4 }, function (error) { 5 // Do something with response error 6 return Promise.reject(error); 7 });
刪除攔截器
1 const myInterceptor = axios.interceptors.request.use(function () {/*...*/}); 2 axios.interceptors.request.eject(myInterceptor);
Axios 二次封裝
- 核心文件
1 /** 2 * @desc: axios封裝 3 * @author: ggw 4 * @module: axios 5 * @description: 配合使用 餓了么的 Message和Loading 6 * 7 */ 8 import axios from 'axios'; 9 import qs from 'qs'; 10 import { 11 Message, 12 Loading 13 } from 'element-ui'; 14 15 import router from '../router'; 16 let loading; 17 let headerNone = { 18 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' 19 }; 20 let headerTwo = { 21 'Content-Type': 'application/json; charset=UTF-8' 22 }; 23 let baseURL = window.location.origin ; 24 25 26 /** 27 * @description: 定義初始化Loading 28 * @method: startLoading 29 */ 30 const startLoading = () => { 31 loading = Loading.service({ 32 target: '.content-box', 33 background: 'rgba(220, 220, 220, 0.51)' 34 }); 35 }; 36 37 38 let count = 0; 39 /** 40 * @description: 顯示Loading 同時多個發送請求 只開啟一次Loading 41 * @method: showLoading && hideLoading 42 */ 43 const showLoading = () => { 44 if (count === 0) startLoading(); 45 count++; 46 }; 47 const hideLoading = () => { 48 if (count <= 0) return; 49 count--; 50 if (count === 0) { 51 setTimeout(() => { 52 loading.close(); 53 }, 300); 54 } 55 }; 56 57 export let filiter = r => { 58 59 for (let item of Object.keys(r)) { 60 if (r[item] === ' ' || r[item] === '') { 61 delete r[item]; 62 } 63 } 64 }; 65 /** 66 * @description: 出口 67 * @exports api 68 * @param:options 必須是對象 69 * options 對象為 axios對應參數 70 */ 71 export default (options) => { 72 /** 73 * @description: 用來初始化承諾的回調。 74 * 這個回調被傳遞了兩個參數: 75 * 一個解析回調用一個值或另一個承諾的結果來解析承諾, 76 * 以及一個拒絕回調,用來拒絕承諾的原因或錯誤。 77 * @constructor: Promise 78 */ 79 return new Promise((resolve, reject) => { 80 const instance = axios.create({ 81 withCredentials: true, 82 headers: headerNone, 83 baseURL 84 }); 85 // 請求攔截器 86 instance.interceptors.request.use(config => { 87 let {load = true} = config.data || config.params || {} ; 88 if (load) showLoading(); 89 // 過濾無值參數 90 if (config.params) { 91 delete config.params.load; 92 filiter(config.params); 93 } else if (config.data) { 94 filiter(config.data); 95 delete config.data.load; 96 } 97 if ( 98 config.method.toLocaleLowerCase() === 'post' || 99 config.method.toLocaleLowerCase() === 'put' 100 ) { 101 // json 格式傳遞 102 if (config.json) { 103 config.headers = headerTwo; 104 } else { 105 config.data = qs.stringify(config.data); 106 config.data = config.data + '&t=' + Date.now(); 107 } 108 } 109 return config; 110 }, error => { 111 hideLoading(); 112 return Promise.reject(error); 113 }); 114 // 響應攔截器 115 instance.interceptors.response.use(response => { 116 setTimeout(hideLoading,0); 117 let data; 118 // IE9時response.data是undefined,因此需要使用response.request.responseText(Stringify后的字符串) 119 if (!response.data ) { 120 data = response.request.responseText; 121 } else { 122 data = response.data; 123 } 124 125 switch (data.code) { // 接口定義字段 126 case '001': 127 Message({ 128 showClose: true, 129 message: data.msg || '未知錯誤,請聯系管理員', 130 type: 'error' 131 }); 132 router.push({ 133 path: '/login' 134 }); 135 break; 136 default: 137 } 138 return data; 139 }, err => { 140 hideLoading(); 141 142 if (err && err.response) { 143 let msg = { 144 400: '請求錯誤', 145 401: '未授權,請登錄', 146 403: '拒絕訪問', 147 404: `請求地址出錯: ${err.response.request.responseURL}`, 148 408: '請求超時', 149 500: '服務器內部錯誤', 150 501: '服務未實現', 151 502: '網關錯誤', 152 503: '服務不可用', 153 504: '網關超時', 154 505: 'HTTP版本不受支持' 155 }; 156 let status = parseInt(err.response.status,10); 157 Message({ 158 showClose: true, 159 message: msg[status] || '', 160 type: 'error' 161 }); 162 } else { 163 Message({ 164 message: err.config ? `請求地址出錯: ${err.config.url}` : err, 165 type: 'error' 166 }); 167 } 168 169 return Promise.reject(err); 170 }); 171 // 請求 172 instance(options) 173 .then(res => { 174 resolve(res); 175 return false; 176 }) 177 .catch(error => { 178 reject(error); 179 }); 180 }); 181 };
- 導出
import axios from './api'; const get = (url, data) => { return axios({ url, method: 'get', params:data }); }; const post = (url, data,json) => { return axios({ url, method: 'post', data, json }); }; const del = (url, data) => { return axios({ url, method: 'delete', params:data }); }; const put = (url, data,json) => { return axios({ url, method: 'put', data, json }); }; export default { get, post, del, put };
- 導入vue main.js
import Vue from 'vue'; import api from './api'; Vue.prototype.$api = api;
- 使用
this.$api.get(url,data,isJson);
