美文网首页JsWeb前端之路让前端飞
前端面试题——手写实现 ajax

前端面试题——手写实现 ajax

作者: ac68882199a1 | 来源:发表于2017-07-08 23:00 被阅读366次

    凡是和后台有过数据交互的小伙伴肯定都接触过 ajax. 我们可以通过 ajax 来实现页面的无刷新请求数据,这样就能在保证良好用户体验的同时,将更多的内容展示给用户

    ajax 在我们的开发工作中已经司空见惯,几乎所有我们频繁使用的库和框架都提供了经过完善封装后的 ajax 方法,如 jQuery、zepto、angular 等等,这使得我们的数据请求变得异常简洁明了

    但是这也带来了很明显的缺陷,就是我们知道如何去使用封装后的 ajax,却不会通过原生的 js 来 ajax,更甚者(如只用过 jQuery 的小伙伴)会将 ajax 与$.ajax()等同

    而这个问题也是面试中常常出现的一题,所以这次就来亲手实现一下原生 js 的 ajax

    XMLHttpRequest对象

    我们常用的 ajax 就是通过 XMLHttpRequest 对象实现的,这个对象有很多的属性和事件,在使用之前,我们需要先将它实例化

    const xhr = new XMLHttpRequest()
    

    实例化后,我们就可以通过 xhr 来发起一个请求

    // xhr 具有一个 open 方法,这个方法的作用类似于初始化,并不会发起真正的请求
    // open 方法具有 5 个参数,但是常用的是前 3 个
    // method: 请求方式 —— get / post
    // url:请求的地址
    // async:是否异步请求,默认为 true(异步)
    xhr.open(method, url, async)
    
    // send 方法发送请求,并接受一个可选参数
    // 当请求方式为 post 时,可以将请求体的参数传入
    // 当请求方式为 get 时,可以不传或传入 null
    // 不管是 get 还是 post,参数都需要通过 encodeURIComponent 编码后拼接
    xhr.send(data)
    

    在通过send方法发送请求后,xhr 对象在收到响应数据时会自动填充到其对应的属性中,xhr 具有以下常用属性:

    responseText: 请求返回的数据内容
    responseXML: 如果响应内容是"text/xml""application/xml",这个属性将保存响应数据的 XML DOM文档
    status: 响应的HTTP状态,如 200 304 404 等
    statusText: HTTP状态说明
    readyStatus: 请求/响应过程的当前活动阶段
    timeout: 设置请求超时时间

    上面写了那么多,我们并没有发现收到数据时的回调方法,那么该怎么在收到数据时进行处理呢?这个问题的重点需要放在readyStatus这个属性上

    readyStatus的值会随着请求各阶段的变化而改变,其一共有 5 个值:

    xhr.readyStatus==0 尚未调用 open 方法
    xhr.readyStatus==1 已调用 open 但还未发送请求(未调用 send)
    xhr.readyStatus==2 已发送请求(已调用 send)
    xhr.readyStatus==3 已接收到请求返回的数据
    xhr.readyStatus==4 请求已完成

    readyStatus的状态发生改变时,会触发 xhr 的事件onreadystatechange,于是我们就可以在这个方法中,对接收到的数据进行处理

    xhr.onreadystatechange = () => {
        if (xhr.readyStatus === 4) {
            // HTTP 状态在 200-300 之间表示请求成功
            // HTTP 状态为 304 表示请求内容未发生改变,可直接从缓存中读取
            if (xhr.status >= 200 && 
                xhr.status < 300 || 
                xhr.status == 304) {
                console.log('请求成功', xhr.responseText)
            }
        }
    }
    

    当网络不佳时,我们需要给请求设置一个超时时间

    // 超时时间单位为毫秒
    xhr.timeout = 1000
    
    // 当请求超时时,会触发 ontimeout 方法
    xhr.ontimeout = () => console.log('请求超时')
    

    以上就是 XMLHttpRequest 对象的基础内容,它还有很多其他的属性和时间,如onerror onabort onload等等,同时 ajax 的跨域、兼容 IE 浏览器等问题我们也并没有提到。对这些感兴趣的小伙伴可以自己 Google 一下,或者等以后有机会,我们再一起来学习这些内容

    下面奉上自己封装的一个极简 ajax 请求方法,在生产项目中不能使用,但是对于临时测试请求个本地文件什么的还是没什么问题的,该 ajax 方法通过 Promise 方式实现回调

    function ajax (options) {
            let url = options.url
            const method = options.method.toLocaleLowerCase() || 'get'
            const async = options.async != false // default is true
            const data = options.data
            const xhr = new XMLHttpRequest()
    
            if (options.timeout && options.timeout > 0) {
                xhr.timeout = options.timeout
            }
    
            return new Promise ( (resolve, reject) => {
                xhr.ontimeout = () => reject && reject('请求超时')
                xhr.onreadystatechange = () => {
                    if (xhr.readyState == 4) {
                        if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {
                            resolve && resolve(xhr.responseText)
                        } else {
                            reject && reject()
                        }
                    }
                }
                xhr.onerror = err => reject && reject(err)
    
                let paramArr = []
                let encodeData
                if (data instanceof Object) {
                    for (let key in data) {
                        // 参数拼接需要通过 encodeURIComponent 进行编码
                        paramArr.push( encodeURIComponent(key) + '=' + encodeURIComponent(data[key]) )
                    }
                    encodeData = paramArr.join('&')
                }
    
                if (method === 'get') {
                      // 检测 url 中是否已存在 ? 及其位置
                    const index = url.indexOf('?')
                    if (index === -1) url += '?'
                    else if (index !== url.length -1) url += '&'
                      // 拼接 url
                    url += encodeData
                }
    
                xhr.open(method, url, async)
                if (method === 'get') xhr.send(null)
                else {
                    // post 方式需要设置请求头
                    xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded;charset=UTF-8')
                    xhr.send(encodeData)
                }
            } )
        }
    

    使用方式

    ajax({
        url: 'your request url',
        method: 'get',
        async: true,
        timeout: 1000,
        data: {
            test: 1,
            aaa: 2
        }
    }).then(
        res => console.log('请求成功: ' + res),
        err => console.log('请求失败: ' + err)
    )
    

    扫描下方二维码,关注前端周记微信公众号。感谢小伙伴们的支持!文中若有任何错误,欢迎指正!一起学习一同进步!

    扫码关注前端周记公众号

    相关文章

      网友评论

        本文标题:前端面试题——手写实现 ajax

        本文链接:https://www.haomeiwen.com/subject/mfnjhxtx.html