Ajax相信大家都不陌生了,从刚开始的前后端分离,我们前端想要从服务器获取数据,就得经过Ajax来获取
let xhr = new XNLHttpRequest();
xhr.open();
xhr.onreadystatechange=function(){
//...
}
xhr.send();
我们大体上会经过这四个步骤来向服务器获取数据,后来估计太麻烦了,JQuery封装了Ajax,使我们获取数据获得了极大的便利
详见jquery文档:jquery.cuishifeng.cn/jQuery.Ajax…
$.ajax({
url:'',
method:'GET',
async:false,
dataType:'json',
success:result=>{
//result:当请求成功执行success函数,result就是从服务器获取的结果
_DATA=result;
}
})
虽然这样少些了很多步骤,但是项目中遇到的Ajax串行,并行,我们要避免回调地狱这个大问题!后来,ES6语法规范中出现了一个新的内置类Promise,我们可以使用then链,来处理JS异步编程。
Axios
Axios是基于Ajax和Promise封装的库,其核心还是Ajax,只是封装的时候用了Promise模式
在后来的项目中,我们会基于Axios向后台接口发送请求。我一般对Axios进行第二次封装。
Axios中文文档:www.axios-js.com/zh-cn/docs/
import axios from 'axios';
import qs from 'qs';
/*
* 根据环境变量进行接口区分
*/
switch (process.env.NODE_ENV) {
case "development":
axios.defaults.baseURL = "http://127.0.0.1:9000";
break;
case "test":
axios.defaults.baseURL = "http://192.168.20.15:9000";
break;
case "production":
axios.defaults.baseURL = "http://api.zhufengpeixun.cn";
break;
}
/*
* 设置超时请求时间
*/
axios.defaults.timeout = 10000;
/*
* 设置CORS跨域允许携带资源凭证
*/
axios.defaults.withCredentials = true;
/*
* 设置POST请求头:告知服务器请求主体的数据格式
*/
axios.defaults.headers['Content-Type'] = 'application/x-www-form-urlencoded';
axios.defaults.transformRequest = data => qs.stringify(data);
/*
* 设置请求拦截器 [ˌɪntərˈsɛptərz]
*/
axios.interceptors.request.use(config => {
// 添加TOKEN验证:可以从本地存储中取值,也可以从VUEX中取值,主要看怎么存储的
// JWT实现TOKEN校验 [ˌɔːθəraɪˈzeɪʃn]
const token = localStorage.getItem('token');
token && (config.headers.Authorization = token);
return config;
}, error => {
return Promise.reject(error);
});
/*
* 设置响应拦截器
*/
axios.defaults.validateStatus = status => {
// 自定义响应成功的HTTP状态码
return /^(2|3)\d{2}$/.test(status);
};
axios.interceptors.response.use(response => {
// 只返回响应主体中的信息(部分公司根据需求会进一步完善,例如指定服务器返回的CODE值来指定成功还是失败)
return response.data;
}, error => {
if (error.response) {
// 请求已发送,只不过状态码不是200系列,设置不同状态码的不同处理
switch (error.response.status) {
case 401: // 当前请求需要用户验证(一般是未登录)
break;
case 403: // 服务器已经理解请求,但是拒绝执行它(一般是TOKEN过期)
localStorage.removeItem('token');
// 跳转到登录页
break;
case 404: // 请求失败,请求所希望得到的资源未被在服务器上发现
break;
}
return Promise.reject(error.response);
} else {
// 断网处理
if (!window.navigator.onLine) {
// 断开网络了,可以让其跳转到断网页面
return;
}
return Promise.reject(error);
}
});
export default axios;
Fetch
Fetch Api:developer.mozilla.org/zh-CN/docs/…
后来浏览器内置的Api:Fetch出现了,它和Ajax不是一个东西,是两套完全不同的机制,并且Fetch天生就是基于Promise进行管理的。
所以现在向服务器发送请求有两种方式:一种是XNLHttpRequest的Ajax方案,第二种就是基于Fetch
fetch([url],[options]).then(response=>{
});
Fetch库的第二次封装
import qs from 'qs';
/*
* 根据环境变量进行接口区分
*/
let baseURL = '';
let baseURLArr = [{
type: 'development',
url: 'http://127.0.0.1:9000'
}, {
type: 'test',
url: 'http://192.168.20.15:9000'
}, {
type: 'production',
url: 'http://api.zhufengpeixun.cn'
}];
baseURLArr.forEach(item => {
if (process.env.NODE_ENV === item.type) {
baseURL = item.url;
}
});
export default function request(url, options = {}) {
url = baseURL + url;
/*
* GET系列请求的处理
*/
!options.method ? options.method = 'GET' : null;
if (options.hasOwnProperty('params')) {
if (/^(GET|DELETE|HEAD|OPTIONS)$/i.test(options.method)) {
const ask = url.includes('?') ? '&' : '?';
url += `${ask}${qs.stringify(params)}`;
}
delete options.params;
}
/*
* 合并配置项
*/
options = Object.assign({
// 允许跨域携带资源凭证 same-origin同源可以 omit都拒绝
credentials: 'include',
// 设置请求头
headers: {}
}, options);
options.headers.Accept = 'application/json';
/*
* token的校验
*/
const token = localStorage.getItem('token');
token && (options.headers.Authorization = token);
/*
* POST请求的处理
*/
if (/^(POST|PUT)$/i.test(options.method)) {
!options.type ? options.type = 'urlencoded' : null;
if (options.type === 'urlencoded') {
options.headers['Content-Type'] = 'application/x-www-form-urlencoded';
options.body = qs.stringify(options.body);
}
if (options.type === 'json') {
options.headers['Content-Type'] = 'application/json';
options.body = JSON.stringify(options.body);
}
}
return fetch(url, options).then(response => {
// 返回的结果可能是非200状态码
if (!/^(2|3)\d{2}$/.test(response.status)) {
switch (response.status) {
case 401: // 当前请求需要用户验证(一般是未登录)
break;
case 403: // 服务器已经理解请求,但是拒绝执行它(一般是TOKEN过期)
localStorage.removeItem('token');
// 跳转到登录页
break;
case 404: // 请求失败,请求所希望得到的资源未被在服务器上发现
break;
}
return Promise.reject(response);
}
return response.json();
}).catch(error => {
// 断网处理
if (!window.navigator.onLine) {
// 断开网络了,可以让其跳转到断网页面
return;
}
return Promise.reject(error);
});
};
在项目中,我一般会对api的请求接口进行模块化管理,例如:
定义统一入口 api.js
import Home from './home';
export default {
Home
};
单独模块管理接口
import axios from './http';//对Axios的第二次封装
export default {
login() {
return axios.post('/login');
},
// ...
};
最后注入到Vue的原型上
import api from './api/api.js';
Vue.prototype.$api=api;
//=>使用
this.$api.Home.login().then(result=>{
//...
});