欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

原生 Ajax 封装 和 Axios 二次 封装

程序员文章站 2022-08-15 08:40:33
AJAX 异步的JavaScript与XML技术( Asynchronous JavaScript and XML ) Ajax 不需要任何浏览器插件,能在不更新整个页面的前提下维护数据,但需要用户允许JavaScript在浏览器上执行。 兼容性 封装 XMLHttpRequest 对象 1 // ......

ajax

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

兼容性

原生 Ajax 封装 和 Axios 二次 封装

 

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

 兼容性

原生 Ajax 封装 和 Axios 二次 封装

 

安装

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