你真得懂AJAX吗

172 阅读6分钟

闲暇时间下,没有事情做,于是寻思着可以重新学习一下之前认识比较浅薄的知识点。
之前项目和实践中都是使用的ajax都是第三方封装好的(jquery或者axios),但是只知道如何使用,其中的细节不得而知。所以要熟悉一下ajax原生解决方案XMLHttpRequest(以下简称XHR)

AJAX大家都不陌生,全称为Asynchronous JavaScript + XML,其本身也不是一个新的技术,而是在现有技术下的一个方法集合, 也就是大家常说的异步JavaScript请求。而XHR是AJAX技术的核心,一种优秀的解决方案。我们借助XHR提供的能力,可以让浏览器在页面不刷新的情况下发起请求和获取资源,提升用户体验,我们今天要探索得就是这个。

XHR的用法

  1. 使用XHR能力前,我们需要先实例化XHR构造函数
    const xhr = new XMLHttpRequest()
  2. 使用xhr对象的open方法,建立一条client与server之间的通道
    open方法需要传入可选的三个参数
  • method AJAX请求的方法,包括POST,GET,PUT等等,注意这里需要大写,不然会存在部分浏览器识别不了问题
  • url AJAX请求的服务器地址
  • async 请求发送是否以异步的方式,默认是true。我们不建议将请求设置为同步,会影响用户的浏览体验
    const xhr  = new XMLHttpRequest()    
    xhr.open('GET','http://jsonplaceholder.typicode.com/users'true)
    
  1. 使用xhr的send方法,传递所需要的参数,真正得发起请求
    open方法需要传入一个可选的参数 data,就是http报文的中请求体,如果是GET方法我们可以传入null

    const xhr  = new XMLHttpRequest()    
    xhr.open('GET','http://jsonplaceholder.typicode.com/users')
    xhr.send(null)
    
  2. 在建立通道之后(open方法),和发送请求之前(send方法),我们可以使用setRequestHeader手动设置部分请求首部,有些字段只能由浏览器代理自己设定。 setRequestHeader方法需要传入两个参数

    1. header 首部字段名称
    2. value 首部值
     const xhr  = new XMLHttpRequest()    
     xhr.open('GET','http://jsonplaceholder.typicode.com/users')
     xhr.setRequestHeader('content-type','application/json')
     xhr.send(null)
    

    如果accept字段没有被手动设置,将会被设置为 '/'

  3. 请求发送之后,该如何监听请求呢?xhr对象有个readyState属性,记录着请求的实时情况。
    readState有五个值

    状态码 具体信息
    0 xhr对象被实例,但是没有调用open方法
    1 xhr对象调用了open方法,建立了通道,但是没有调用send方法
    2 调用了send方法,请求已经发送,但是没有接受到响应
    3 接收到部分响应数据
    4 接受到完全的响应数据

    xhr对象提供了一个状态改变时的事件监听机制 readystatechange
    我们可以在xhr对象的readyState变为4时,进行请求成功处理

    const xhr  = new XMLHttpRequest()    
    xhr.onreadystatechange = function () {
        if(xhr.readyState === 4){
            // 处理请求结果
        }
    }
    xhr.open('GET','http://jsonplaceholder.typicode.com/users')
    xhr.setRequestHeader('content-type','application/json')
    xhr.send(null)
    

    请求成功时,相关的请求信息会被附加在xhr对象上,简单介绍一下常用的属性

    属性 含义
    status 响应状态码
    statusText 响应状态声明
    response 响应返回的数据(即请求的响应体,具体类型取决responseType)
    responseType 响应返回数据的类型

    注意: responseType 可以请求发送前手动设置类型,默认是'',表现形式与text一致。
    如果服务器返回的数据类型和我们设置的类型不兼容,那么response将会返回null。

  4. 其他用法

    1. timeout 可以在open方法之后,send之前设定好请求的超时时间(单位为ms),默认为0
      请求超时会触发timeout 事件
    2. abort 使用xhr对象的abort方法可以在请求成功之前中止请求
      中止请求会触发,abort事件
    3. onload XHR现在支持请求成功后触发load事件,也意味着我们不需要去监听的请求的状态,可以在触发load事件时处理
    4. progress progress事件会在浏览器接收数据时不断被触发(适用于接收图片,视频),progress事件会传入一个event对象,其中包含四个属性:
      • target 事件所属的xhr对象
      • lengthComputable 进度信息是否可以的布尔值
      • position 已结接收的字节数
      • totalSize 通过content-length计算出的数据预期总字节数
        我们可以通过这些属性,创建一个数据传输时的实时进度条

CORS(跨域资源共享)

由于浏览器的同源策略,浏览器会限制跨域请求(origin不同指的是 协议/主机名/端口号 不同)。
跨域资源共享(CORS) 是一种安全的机制,它使用额外的 HTTP 头来告诉浏览器,允许client可以向server发起不被限制的跨域请求,从而使得数据安全有效的传输。

CORS新增了一组http首部字段,允许服务器声明哪些第三方域可以请求资源。
CORS策略会对某些对服务器会产生副作用的请求(不是简单请求)强制先使用options方法发起preflight(预检请求),判断是否可以发起真正得跨域请求。 简单请求的判定参考mdn
如果服务器允许,然后发送真正得请求,服务器也可以告知客户端是否可以携带身份凭证(cookies或其他认证信息)

  1. 简单请求
    如果跨域请求是一个简单请求,那么只需要后端将 Access-Control-Allow-Origin设置为允许访问域或者*(通配符)
  2. 预检请求
    跨域请求不是简单请求,需要先发起预检请求使用options方法,且携带两个头部
    Access-Control-Request-Methods(真正请求的方法)
    Access-Control-Request-Headers(真正请求修改过的请求头首部)
    服务器会根据预检请求的请求信息进行响应,预检请求的响应首部一般包括如下信息
    Access-Control-Allow-Origin 允许跨域的源
    Access-Control-Allow-Methods 跨域访问允许的方法
    Access-Control-Request-Headers 跨域访问允许修改的请求首部
    Access-Control-Max-Age 预检请求与跨域请求之间的最大等待时间,等待时间内再次发起跨域请求不需要预检请求
  3. 附带身份凭证(cookie等)
    发起xhr跨域请求时,设置withCredentials 为true,才能将凭证信息发送给服务器。
    跨域响应会会包含Access-Control-Allow-Credentials首部,当xhr请求设置withCredentials 为true时,可以让浏览器读取response信息。(预检请求这个首部是告知浏览器是否可以使用凭证)