原生 Ajax 封装 和 Axios 二次 封装

2019-05-23 09:55:11来源:博客园 阅读 ()

新老客户大回馈,云服务器低至5折

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 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. 使用 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 监听了一个数据传输完成的事件。

 
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 的 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);

 


原文链接:https://www.cnblogs.com/gaoguowen/p/10862914.html
如有疑问请与原作者联系

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:Vue的生命周期

下一篇:vue3.0学习笔记(二)