原生 Ajax 封装 和 Axios 二次 封装

匿名 (未验证) 提交于 2019-12-02 23:34:01

AJAX

异步的JavaScript与XML技术( Asynchronous JavaScript and XML )
Ajax 不需要任何浏览器插件,能在不更新整个页面的前提下维护数据,但需要用户允许JavaScript在浏览器上执行。

兼容性

封装 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 2XMLHttpRequest

可以设置 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);
可以上传文件
  • FormDatablob、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 以下不支持)
  • 跨域资源共享Access-Control-Allow-Origin
  • CORScookieAccess-Control-Allow-Credentials: true,
     xhr.withCredentials = true;
可以获取服务端二进制数据

overrideMimeTypeMIME

1 // 参数 MIME 类型 2 // 告诉浏览器,服务器响应的内容是用户自定义的字符集  3 xhr.overrideMimeType('text/plain; charset=x-user-defined'); 4 // 浏览器就会将服务器返回的二进制数据当成文本处理,我们需要做进一步的转换才能拿到真实的数据 5   // 获取二进制数据的第 i 位的值 6   var byte = xhr.responseText.charCodeAt(i) & 0xff

阮一峰的文章

2.xhr.responseType 用于设置服务器返回的数据的类型,将返回类型设置为 blob 或者 arraybufferxhr.response

1   xhr.responseType = 'arraybuffer' 2   xhr.onload = function () { 3   var arrayBuffer = xhr.response 4   // 接下来对 arrayBuffer 做进一步处理... 5   }

参考资料

使用 onload 监听了一个数据传输完成的事件。

1 // 上传进度监听 2 xhr.upload.addEventListener('progress', onProgressHandler, false); 3  4 // 传输成功完成 5 xhr.upload.addEventListener('load', onLoadHandler, false); 6 // 传输失败信息 7 xhr.upload.addEventListener('error', onErrorHandler, false);

阮一峰的文章MDN

AXIOS

  • Promise
  • 可以在客户端 和 nodeJs中使用
  • 在客户端创基 XMLHttpRequests
  • 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);

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!