ajax
说到前端开发工作,已经绕不开的一个点就是ajax,如果你的项目是前后端分离的,那么如何封装ajax,如何请求后端接口简直就是日常不能再日常的工作了。
目前我们比较常用的就是axios组件,简直好用到爆。
有兴趣了解源码的同学可以看看下面的链接:
我们日常进行ajax请求 都是什么样的操作呢?
//简单封装下xhr
const ajax = (url, onSuccess, onFail) => {
// 判断支不支持xmlhttprequest 方法
const xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject();
// 连接服务器
xhr.open("get", url, true)
// 发送
xhr.send();
// 默认带cookie
// xhr.withCredentials = true;
// 接收
xhr.onreadystatechange = () =>{
if(xhr.readyState ===4){//代表请求完成
if(xhr.status ===200){ //代表请求成功
onSuccess && onSuccess(xhr.responseText)
}else{
onFail && onFail()
}
}
}
}
ajax('http://127.0.0.1:8080/demo.json',(e)=>{console.log(e)})
上面我们简单的封装了下xhr,当然还有很多细节没有打磨,但是这个可以初步的进行ajax访问,并将请求到的数据进行打印了。
当然js原生提供了fetch方法,这个大家可以自己去看下MDN: fetch
这里我们只了解封装ajax的有哪些实现。
像axios会提供给我们接口让我们来实现对请求的拦截和对响应的拦截,接下来我们简单实现一下。
// 创建全局变量,用来接收订阅的参数
let cc = null;
// 创建一个发布订阅对象
const mitt = {
cache:{},
on(name,func){
this.cache[name] = func //这个函数 “有参数(onCancel)”
},
emit(name,data){
const fn = this.cache[name]
fn && fn(data) //这个data就是下面传过来的onCancel函数 ↑
}
}
//定义一个函数用来下面请求ajax
const adaptor = (config)=>{
// axios我们用过的同学知道它返回一个promise 对象,这里我们也返回一个promise
return new Promsie((resolve,reject)=>{
let request = new XMLHttpRequest();
// 创建http请求
request.open(
config.methods.toUpperCase(),
config.url,
true
)
request.send();
request.timeout = cnofig.timeout
request.onabort = () => {
// 请求被终止时候调用
reject('[abort] request is abort.')
request = null
}
if(config.cancel){
// 监听订阅,将回调函数传递给cache.abort ↓(下面这个函数)
mitt.on("emit",function onCancel(){
request.abort()
request = null
reject('【abort】current request is abort.')
})
}
// 请求状态发生变化时候的回调
request.onreadystatechange = () =>{
if(request.readyState==4&&request.status==200){
// 这里模拟异步任务,请求时间
setTimeout(() => {
return resolve(request && request.responseText)
}, 5000);
}else{
reject('request error')
}
}
})
}
// dispatchRequest 用来发起请求,具体请求方法在adaptor中
const dispatchRequest = (config) =>{
return adaptor(config).then( (response)=>{
// 返回结果
return response
},(reason)=>{
// 抛出异常
Promise.reject(reason )
})
}
//真正调用的方法在这里
const req = (config = {}) =>{
// 如何实现拦截
const chain = [dispatchRequest,undefined]
// 参数中存在请求拦截,则将请求拦截函数存储在chain中,以便后续promise链式调用
if (config.interceptor) {
chain.unshift(
config.interceptor.onFulfilled,
config.interceptor.onReject
)
}
if(config.cancel){
// 如果请求被取消的话
// 发布订阅,abort -> func
// cache.abort = func ↓(下面这个函数)
mitt.on('abort',function(cancel){ //这里的cancel就是传进来的onCancel函数
// 执行config.cancel
config.cancel(cancel)
})
}
// 参数中存在响应拦截,则将请求拦截函数存储在chain中,以便后续promise链式调用
if (config.adaptor) {
chain.unshift(
config.adaptor.onFulfilled,
config.adaptor.onReject
)
}
// [拦截器成功,拦截器失败,请求成功,请求失败,响应成功,响应失败]
// proise 链式调用,改变promise状态为fulfilled
let promise = Promise.resolve(config)
// 判断chain中是否还有参数,并循环调用
while (chain.length) {
promise = promise.then(chain.shift(),chain.shift())
}
return promise;
}
//测试调用
req({
url: 'http://127.0.0.1:8080/demo.json',
method: 'get',
interceptor: {
fullfilled: e => {
console.log('请求拦截成功', e);
return e;
}
},
adaptor: {
fullfilled: e => {
console.log('响应拦截成功', e);
return e;
}
},
// 这里存在cancel ,所以两秒之后会取消拦截
cancel(onCancel) {
cc = onCancel;
}
})
setTimeout(() => {
cc && cc() //这里阻断请求继续进行,不继续进行数据请求
}, 2000);
大家也可以简单实现一下axios。