当前位置: 移动技术网 > IT编程>开发语言>JavaScript > 原生 Ajax 封装 和 Axios 二次 封装

原生 Ajax 封装 和 Axios 二次 封装

2019年05月23日  | 移动技术网IT编程  | 我要评论

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

 

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网