原生Ajax的封装 和 Axios的 二次封装

AJAX

异步的JavaScript与XML技术( Asynchronous JavaScript and XML )
Ajax 核心使用 XMLHttpRequest (XHR)对象,首先由微软引入的一个特性;Ajax 不需要任何浏览器插件,能在不更新整个页面的前提下维护数据(可以向服务器请求额外的数据无需重载页面),但需要用户允许JavaScript在浏览器上执行。

XHR 对象用法

1
var xhr = new XMLRequestHttp() // 通过XMLHttpRequest 构造函数来创建

open 方法

1
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

setRequestHeader()

如需设置 Accpet 头部信息,可通过setRequestHeader() 方法来设置

  • Accpet 头部信息:告知客户端可以处理的内容类型,用 MIME类型 表示;服务端使用 Content-Type 通知客户端它的选择
    媒体类型( MIME类型 ) :一种标准,用来表示文档、文件或字 节流的性质和格式。 完整类型列表
    • Content-Type :实体头部用于指示资源的 MIME 类型,告诉客户端实际返回的内容类型;浏览器会在某些情况下进行MIME查找,并不一定遵循此标题的值; 为了防止这种行为,可以将标题 X-Content-Type-Options 设置为 nosniff
      1
      xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded')

send 方法

data:作为请求主体发送的数据,如果不需要通过请求主体发送数据,则必须传 null
调用 send()发送请求,在收到响应后,响应的数据会自动填充XHR对象的属性

1
xhr.send(data);

responseText :从服务端返回的文本

1
2
3
4
5
6
xhr.onload = function () {
if (xhr.readyState === xhr.DONE) {
if (xhr.status === 200) {5 console.log(xhr.responseText);
}
}
};

responseXML

如果响应的 Content-Typetext/htmlapplication/xml,将保存包含响应数据的 XML DOM 文档,对于其它类型的数据则为 null, 也可通过overrideMimeType() 强制 XHR 对象解析为 XML

1
2
3
4
5
6
7
8
9
// overrideMimeType() 用来强制解析 response 为 XML
xhr.overrideMimeType('text/xml');

xhr.onload = function () {
if (xhr.readyState === xhr.DONE) {
if (xhr.status === 200) { 8 console.log(xhr.responseXML);
}
}
};

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
2
3
4
5
xhr.onreadystatechange = function () {
if(xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
console.log(xhr.responseText)
}
}

参考资料

封装示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
// 创建 构造函数
function Ajax(obj) {
this.url = obj.url ||'';
this.type = obj.type || 'get';
this.data = obj.data ||{};
this.success = obj.success || null;
this.error = obj.error || null;
}
// 再原型上创建方法
Ajax.prototype.send = function(){
var self = this;
var toStr = Object.prototype.toString;
if (self.data === null && typeof self.data !== 'object' && Array.isArray(obj)) return;
return (function(){
// 实例化 XML对象
var xhr = new XMLHttpRequest();
var data = '';
// 序列化参数
for (var k in self.data){
data += k + '=' + self.data[k] + '&';
}
data = data.substr(0,data.length - 1);
// 接收回调函数
xhr.onreadystatechange = function(){
if (xhr.readyState === 4){
if(xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {
isFunction(self.success) && self.success(xhr.responseText)
}else{
isFunction(self.error) && self.error(xhr)
}
}
}
// 初始化请求
if(self.type.toLocaleLowerCase() === 'post'){
xhr.open ('post',self.url,true)
// 设置请求头
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
//发送请求
xhr.send(data)
} else {
xhr.open('get', self.url + "?" + data,true)
xhr.send(null)
}
}());
};

function isFunction(obj){
return toStr.call(obj) === "[object Function]"
}

var ajax = new Ajax({
type:'post',
url:"/login",
data:{
loginname:"admin",
password:"admin"
},
success:function(e){
console.log(e)
},
error:function(err){
console.log(err)
},
}).send();

XMLHttpRequest Level 2 相比于 老版本的 XMLHttpRequest 新增以下内容:

可以设置 HTTP 请求超时时间
1
2
3
4
5
6
7
8
9
var xhr = XMLHttpRequest();
xhr.open('GET','url');
// 超时 2s
xhr.timeout = 2000;
// 超时处理
xhr.ontimeout = function(e) {
console.log(e)
}
xhr.send(null)
可以通过 FormData 发送表单数据
1
2
3
4
5
6
7
  // 实例化 FormData
var formData = new FormData();
// 添加数据
formData.append(key,value);

xhr.open('POST','url');
xhr.send(formData);
可以上传文件
  • FormData 除了可以添加字符串数据,也可以添加 blob、file 类型的数据,因此可以用于上传文件。
  • 在浏览器中,一般是通过文件上传输入框来获取 file 对象,比如:

    1
    <input type="file" name='uploadFile' id="upload-file" />
    1
    2
    3
    4
    5
    6
    7
    8
    document.getElementById('upload-file')
    .addEventListener('change', function () {

    var formData = new FormData();
    // 获取数据
    formData.append('uploadFile', this.files[0])
    xhr.send(formData)
    })
支持跨域请求
  • 浏览器默认是不允许跨域请求的,有时候又是必要的,在以前通常使用JSONP来解决(IE10 以下不支持)
  • 为了标准化跨域请求, W3C提出 跨域资源共享(CORS)前端无须修改代码,只需 服务器返回 Access-Control-Allow-Origin 响应头,指定允许对应的域,如果是公共资源可指定“*”
  • CORS 默认不发送 cookie 如果需要发送,前端需要设置 withCredentials属性,同时服务器需要 返回 Access-Control-Allow-Credentials: true

    1
    xhr.withCredentials = true;

    检测XHR是否支持CORS最简单的方式,就是检查是否存在 withCredentials,再检测XDomainRequest 对象是否存在,即可兼顾所有浏览器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    let createCORSRequest = (method,url)=>{
    let var xhr = mew XMLHttpRequest();
    if ('withCredentials' in xhr){
    xhr.open(method,url,true);
    }else if(typeof XDomainRequest != 'undefined'){
    xhr = new XDomainRequest();
    xhr.open(method,url);
    }else{
    xhr = null
    }
    return xhr
    }
    let request = createCORSRequest('get','baidu.com')
    if(request){
    request.onload = function(){
    // request.responseText
    }
    request.send()
    }

    Preflighted Requests:

  • 一个透明服务器验证机制,用于检查服务器是否支持CORS
    这是一个 OPTIONS 请求,使用了三个请求头
  • Access-Control-Request-Method:请求自身使用的方法
  • Access-Control-Request-Headers:自定义头部信息,多个头部以逗号分隔
  • Origin报头:和简单请求相同,将请求的域发送给服务端,服务端再Access-Control-Allow-Origin 响应头中返回同样的域即可解决跨域问题。

img src特性:

  • 一个网页可以从任何网页中加载图像,不用担心跨域问题,通过onload 和 onerror 事件处理确定是否接收到响应
  • 请求的数据通过查询字符串形式发送,响应可以是任意内容,通常是像素图或204响应。
  • 只能发送 get 请求,无法访问服务器的响应文本
    1
    2
    3
    4
    5
    let img = new Image();
    img.onload = function (){
    console.log('done')
    }
    img.src = 'http://www.baidu.com?test=test1'

请求数据

可以获取服务端二进制数据
  1. 使用 overrideMimeType 方法覆写服务器指定的 MIME 类型,从而改变浏览器解析数据的方式
    1
    2
    3
    4
    5
    6
    // 参数 MIME 类型
    // 告诉浏览器,服务器响应的内容是用户自定义的字符集
    xhr.overrideMimeType('text/plain; charset=x-user-defined');
    // 浏览器就会将服务器返回的二进制数据当成文本处理,我们需要做进一步的转换才能拿到真实的数据
    // 获取二进制数据的第 i 位的值
    var byte = xhr.responseText.charCodeAt(i) & 0xff
  1. xhr.responseType 用于设置服务器返回的数据的类型,将返回类型设置为 blob 或者 arraybuffer,然后就可以从 xhr.response 属性获取到对应类型的服务器返回数据。
    1
    2
    3
    4
    5
    xhr.responseType = 'arraybuffer'
    xhr.onload = function () {
    var arrayBuffer = xhr.response
    // 接下来对 arrayBuffer 做进一步处理...
    }
可以获取数据传输进度信息 参考资料
  • 使用 onload 监听了一个数据传输完成的事件。
    1
    2
    3
    4
    5
    6
    7
    // 上传进度监听
    xhr.upload.addEventListener('progress', onProgressHandler, false);

    // 传输成功完成
    xhr.upload.addEventListener('load', onLoadHandler, false);
    // 传输失败信息
    xhr.upload.addEventListener('error', onErrorHandler, false);

兼容性

XMLHttpRequest

更多资料参考:
阮一峰的文章
MDN


Axios

  • 基于 Promise 的 Http 库
  • 可以在客户端 和 nodeJs中使用
  • 在客户端创基 XMLHttpRequests
  • 在nodeJs 创建 HTTP 请求
  • 支持Promise
  • 可拦截转化请求和响应数据
  • 取消请求
  • 自动转化JSON数据
  • 支持客户端 XSRF

兼容性

axios

安装

1
npm install axios

methods

Get
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const axios = require('axios')

axios.get('url?id=xxx')
.then(res => {
console.log(res)
})
.catch(err =>{
console.log(err)
})
//or
axios.get('url',{
params:{
id:'xxxxx'
}
})
.then(res =>{
console.log(res)
})
.catch(err =>{
console.log(err)
})
  • 同样的传参方法有 delete

    post
    1
    2
    3
    4
    5
    6
    7
    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
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180

    axios({
    method:'get', // default is get
    url:'url', // request url
    data:{ // 仅支持post,put和patch方法,数据作为请求主体发送 ( Only the post,put and patch methods are supported, and the data is sent as the request body )
    /* 浏览器仅支持传递 FormData, File, Blob (The browser only supports passing FormData, File and Blob)
    Node 仅支持传递 Stream, Buffer (The Node only supports passing Stream, Buffer)
    */
    name:'owen'
    },
    baseURL:'base/url', // 除非url是绝对路径,否则将baseURL添加到url的前面 (Add baseURL to then front of the url unless the url is an absolute path)
    transformRequest: [function (data, headers) {
    // 可以修改发送的请求数据和请求头,只支持put,post和patch,回调函数必须返回Buffer,ArrayBuffer,FormData或Stream数据
    // Can modify the sent request data and request header,only support put, post and patch.
    // Callback must return Buffer, ArrayBuffer, FormData or Stream data

    // Do whatever you want to transform the data

    return data;
    }],
    transformResponse: [function (data) {
    // 修改响应数据,再传递给 then或catch 方法 (Modify the response data and pass it to the then or catch method)
    // Do whatever you want to transform the data

    return data;
    }],
    headers: {'X-Requested-With': 'XMLHttpRequest'}, // 自定义请求头 (Custom request header)
    params:{ // 添加到url尾部的参数,一般用于get 和 delete( Parameters addde to the end of the url,generally used for get and delete )
    id:'xxx'
    },
    paramsSerializer: function (params) { //序列化 [params] (https://www.npmjs.com/package/qs)
    return Qs.stringify(params, {arrayFormat: 'brackets'})
    },
    timeout:1000,// default is 0 , 设置请求超时时间,单位毫秒 ( Set request timeout in milliseconds )
    withCredentials: true, // default is false, 跨域时是否携带cookie( Whether to carry cookies when crossing domains )
    adapter: function (config) {
    /*拦截响应数据*/
    // At this point:
    // - config has been merged with defaults
    // - request transformers have already run
    // - request interceptors have already run

    // Make the request using config provided
    // Upon response settle the Promise
    return new Promise(function(resolve, reject) {

    var response = {
    data: responseData,
    status: request.status,
    statusText: request.statusText,
    headers: responseHeaders,
    config: config,
    request: request
    };

    settle(resolve, reject, response);

    // From here:
    // - response transformers will run
    // - response interceptors will run

    /**
    * Resolve or reject a Promise based on response status.
    *
    * @param {Function} resolve A function that resolves the promise.
    * @param {Function} reject A function that rejects the promise.
    * @param {object} response The response.
    */
    function settle(resolve, reject, response) {
    var validateStatus = response.config.validateStatus;
    if (!validateStatus || validateStatus(response.status)) {
    resolve(response);
    } else {
    reject(createError(
    'Request failed with status code ' + response.status,
    response.config,
    null,
    response.request,
    response
    ));
    }
    };
    /**
    * Create an Error with the specified message, config, error code, request and response.
    *
    * @param {string} message The error message.
    * @param {Object} config The config.
    * @param {string} [code] The error code (for example, 'ECONNABORTED').
    * @param {Object} [request] The request.
    * @param {Object} [response] The response.
    * @returns {Error} The created error.
    */
    function createError(message, config, code, request, response) {
    var error = new Error(message);
    return enhanceError(error, config, code, request, response);
    }

    /**
    * Update an Error with the specified config, error code, and response.
    *
    * @param {Error} error The error to update.
    * @param {Object} config The config.
    * @param {string} [code] The error code (for example, 'ECONNABORTED').
    * @param {Object} [request] The request.
    * @param {Object} [response] The response.
    * @returns {Error} The error.
    */
    function enhanceError(error, config, code, request, response) {
    error.config = config;
    if (code) {
    error.code = code;
    }

    error.request = request;
    error.response = response;
    error.isAxiosError = true;

    error.toJSON = function() {
    return {
    // Standard
    message: this.message,
    name: this.name,
    // Microsoft
    description: this.description,
    number: this.number,
    // Mozilla
    fileName: this.fileName,
    lineNumber: this.lineNumber,
    columnNumber: this.columnNumber,
    stack: this.stack,
    // Axios
    config: this.config,
    code: this.code
    };
    };
    return error;
    }
    });
    },
    auth:{ // 表示应使用HTTP Basic身份验证,并提供凭据 ( indicates that HTTP Basic auth should be used, and supplies credentials. )
    user:'xxx',
    password:'***'
    },
    responseType: 'json',/* 服务器响应的数据类型( The server response data type )
    支持 arraybuffer, blob, document, json, text, stream
    */
    responseEncoding:'utf8', // 用于解码响应的编码 (Encoding for decoding the response )
    xsrfCookieName: 'XSRF-TOKEN', // default is XSRF-TOKEN , csrf令牌Cookie 名称
    xsrfHeaderName: 'X-XSRF-TOKEN', //default is X-XSRF-TOKEN, xsrf标记值的http标头的名称
    onUploadProgress: function (progressEvent) { //上传进度事件 (handling of progress events for uploads )
    console.log(progressEvent)
    },
    onDownloadProgress: function (progressEvent) { // 下载进度事件 ( handling of progress events for downloads)
    console.log(progressEvent)
    },
    maxContentLength: 2000, // 允许响应内容的最大字节 (defines the max size of the http response content in bytes allowed)
    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` )
    return status >= 200 && status < 300; // default
    },
    maxRedirects: 5, // default is 5 // 定义Node 中最大重定向数 ( defines the maximunn number of redirects in Node )
    socketPath: null, // default is null 定义要在node.js中使用的 UNIX socket
    httpAgent: new http.Agent({ keepAlive: true }), // node 中 http 和 https 的代理
    httpsAgent: new https.Agent({ keepAlive: true }),// http://nodejs.cn/api/http.html
    proxy: { // 代理配置
    host: '127.0.0.1',
    port: 9000,
    auth: {
    username: 'mikeymike',
    password: 'rapunz3l'
    }
    },
    cancelToken: new CancelToken(function (cancel) { // 取消请求的 token
    })
    })
    .then(res =>{
    console.log(res)
    })
    .catch(err =>{
    console.log(err)
    })
全局配置
  • 通过 axios.create 方法来替换全局配置

    1
    2
    3
    const instance = axios.create({
    baseURL: 'base/url'
    });
  • 通过axios.defaults 对象替换全局默认配置

    1
    2
    instance.defaults.headers.common['Authorization'] = AUTH_TOKEN;
    instance.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
拦截器
  • 拦截请求前的数据

    1
    2
    3
    4
    5
    axios.interceptors.request.use(function (config) {
    return config;
    }, function (error) {
    return Promise.reject(error);
    });
  • 拦截响应数据

    1
    2
    3
    4
    5
    6
    7
    axios.interceptors.response.use(function (response) {
    // Do something with response data
    return response;
    }, function (error) {
    // Do something with response error
    return Promise.reject(error);
    });
  • 删除拦截器

    1
    2
    const myInterceptor = axios.interceptors.request.use(function () {/*...*/});
    axios.interceptors.request.eject(myInterceptor);

二次封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
/**
* @desc: axios封装
* @author: ggw
* @module: axios
* @description: 配合使用 饿了么的 Message和Loading
* @Date: 2018
*/
import axios from 'axios';
import qs from 'qs';
import {
Message,
Loading
} from 'element-ui';

import router from '../router';
let loading;
let headerNone = {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
};
let headerTwo = {
'Content-Type': 'application/json; charset=UTF-8'
};
let baseURL = window.location.origin ;


/**
* @description: 定义初始化Loading
* @method: startLoading
*/
const startLoading = () => {
loading = Loading.service({
target: '.content-box',
background: 'rgba(220, 220, 220, 0.51)'
});
};


let count = 0;
/**
* @description: 显示Loading 同时多个发送请求 只开启一次Loading
* @method: showLoading && hideLoading
*/
const showLoading = () => {
if (count === 0) startLoading();
count++;
};
const hideLoading = () => {
if (count <= 0) return;
count--;
if (count === 0) {
setTimeout(() => {
loading.close();
}, 300);
}
};

export let filiter = r => {

for (let item of Object.keys(r)) {
if (r[item] === ' ' || r[item] === '') {
delete r[item];
}
}
};
/**
* @description: 出口
* @exports api
* @param:options 必须是对象
* options 对象为 axios对应参数
*/
export default (options) => {
/**
* @description: 用来初始化承诺的回调。
* 这个回调被传递了两个参数:
* 一个解析回调用一个值或另一个承诺的结果来解析承诺,
* 以及一个拒绝回调,用来拒绝承诺的原因或错误。
* @constructor: Promise
*/
return new Promise((resolve, reject) => {
const instance = axios.create({
withCredentials: true,
headers: headerNone,
baseURL
});
// 请求拦截器
instance.interceptors.request.use(config => {
let {load = true} = config.data || config.params || {} ;
if (load) showLoading();
// 过滤无值参数
if (config.params) {
delete config.params.load;
filiter(config.params);
} else if (config.data) {
filiter(config.data);
delete config.data.load;
}
if (
config.method.toLocaleLowerCase() === 'post' ||
config.method.toLocaleLowerCase() === 'put'
) {
// json 格式传递
if (config.json) {
config.headers = headerTwo;
} else {
config.data = qs.stringify(config.data);
config.data = config.data + '&t=' + Date.now();
}
}
return config;
}, error => {
hideLoading();
return Promise.reject(error);
});
// 响应拦截器
instance.interceptors.response.use(response => {
setTimeout(hideLoading,0);
let data;
// IE9时response.data是undefined,因此需要使用response.request.responseText(Stringify后的字符串)
if (!response.data ) {
data = response.request.responseText;
} else {
data = response.data;
}

switch (data.code) { // 接口定义字段
case '001':
Message({
showClose: true,
message: data.msg || '未知错误,请联系管理员',
type: 'error'
});
router.push({
path: '/login'
});
break;
default:
}
return data;
}, err => {
hideLoading();

if (err && err.response) {
let msg = {
400: '请求错误',
401: '未授权,请登录',
403: '拒绝访问',
404: `请求地址出错: ${err.response.request.responseURL}`,
408: '请求超时',
500: '服务器内部错误',
501: '服务未实现',
502: '网关错误',
503: '服务不可用',
504: '网关超时',
505: 'HTTP版本不受支持'
};
let status = parseInt(err.response.status,10);
Message({
showClose: true,
message: msg[status] || '',
type: 'error'
});
} else {
Message({
message: err.config ? `请求地址出错: ${err.config.url}` : err,
type: 'error'
});
}

return Promise.reject(err);
});
// 请求
instance(options)
.then(res => {
resolve(res);
return false;
})
.catch(error => {
reject(error);
});
});
};
--------------------- Thank you for reading ---------------------