js基础_02_ajax

98 阅读3分钟

ajax

说到前端开发工作,已经绕不开的一个点就是ajax,如果你的项目是前后端分离的,那么如何封装ajax,如何请求后端接口简直就是日常不能再日常的工作了。

目前我们比较常用的就是axios组件,简直好用到爆。

有兴趣了解源码的同学可以看看下面的链接:

axios

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。