原生 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在浏览器上执行。
兼容性
封装 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);
上一篇: 亲密接触ASP.Net(3)
下一篇: 一个判断js数据类型的函数