15-XMLHttpRequest如何实现

556 阅读4分钟

XMLHttpRequest如何实现

XMLHttpRequest出现之前,服务器数据如果有更新,就要重新刷新整个页面

XMLHttpRequest出现之后,既可以通过XMLHttpRequest请求服务器提供的接口,获取服务器的数据

这样只需要部分刷新,不需要全部刷新DOM

回调函数

回调函数有同步回调和异步回调:

  • 同步回调:回调函数 callback 是在主函数doWork 返回之前执行的,我们把这个回调过程称为同步回调

     let callback = function(){
         console.log('i am do homework')
     }
     function doWork(cb) {
         console.log('start do work')
         cb()
         console.log('end do work')
     }
     doWork(callback)
    
  • 异步回调:回调函数在主函数外部执行的过程称为异步回调

     let callback = function(){
         console.log('i am do homework')
     }
     function doWork(cb) {
         console.log('start do work')
         setTimeout(cb,1000)   
         console.log('end do work')
     }
     doWork(callback)
    

XMLHttpRequest的运作机制

XMLHttpRequset的工作流程如下:

XMLHttpRequest执行流程.png

接着我们分析一下从发起请求到接收数据的完整流程:

  function GetWebData(URL){
     /**
      * 1: 新建 XMLHttpRequest 请求对象
      */
     let xhr = new XMLHttpRequest()
  
     /**
      * 2: 注册相关事件回调处理函数 
      */
     xhr.onreadystatechange = function () {
         switch(xhr.readyState){
           case 0: // 请求未初始化
             console.log(" 请求未初始化 ")
             break;
           case 1://OPENED
             console.log("OPENED")
             break;
           case 2://HEADERS_RECEIVED
             console.log("HEADERS_RECEIVED")
             break;
           case 3://LOADING  
             console.log("LOADING")
             break;
           case 4://DONE
             if(this.status == 200||this.status == 304){
                 console.log(this.responseText);
             }
             console.log("DONE")
             break;
         }
     }
  
     xhr.ontimeout = function(e) { console.log('ontimeout') }
     xhr.onerror = function(e) { console.log('onerror') }
  
     /**
      * 3: 打开请求
      */
     xhr.open('Get', URL, true);// 创建一个 Get 请求, 采用异步
  
  
     /**
      * 4: 配置参数
      */
     xhr.timeout = 3000 // 设置 xhr 请求的超时时间
     xhr.responseType = "text" // 设置响应返回的数据格式
     xhr.setRequestHeader("X_TEST","time.geekbang")
  
     /**
      * 5: 发送请求
      */
     xhr.send();
 }

第一步:创建 XMLHttpRequest 对象

当执行到let xhr = new XMLHttpRequest()后,JavaScript 会创建一个 XMLHttpRequest 对象xhr,用来执行实际的网络请求操作

第二步:为 xhr 对象注册回调函数

因为网络请求比较耗时,所以要注册回调函数,这样后台任务执行完成之后就会通过调用回调函数来告诉其执行结果

XMLHttpRequest 的回调函数主要有下面几种:

ontimeout,用来监控超时请求,如果后台请求超时了,该函数会被调用;

onerror,用来监控出错信息,如果后台请求出错了,该函数会被调用;

onreadystatechange,用来监控后台请求过程中的状态,比如可以监控到 HTTP 头加载完成的消息、HTTP 响应体消息以及数据加载完成的消息等。

第三步:配置基础的请求信息

注册好回调事件之后,接下来就需要配置基础的请求信息了,首先要通过 open 接口配置一些基础的请求信息,包括请求的地址、请求方法和请求方式(同步还是异步请求)

然后通过 xhr 内部属性类配置一些其他可选的请求信息,你可以参考文中示例代码,我们通过xhr.timeout = 3000来配置超时时间,也就是说如果请求超过3000 毫秒还没有响应,那么这次请求就被判断为失败了。

我们还可以通过xhr.responseType = "text"来配置服务器返回的格式,将服务器返回的数据自动转换为自己想要的格式,如果将responseType的值设置为json,那么系统会自动将服务器返回的数据转换为 JavaScript对象格式

假如还需要添加自己专用的请求头属性,可以通过 xhr.setRequestHeader 来添加

第四步:发起请求。

一切准备就绪之后,就可以调用xhr.send来发起网络请求

你可以对照上面那张请求流程图,可以看到:渲染进程会将请求发送给网络进程,然后网络进程负责资源的下载,等网络进程接收到数据之后,就会利用 IPC 来通知渲染进程渲染进程接收到消息之后,会将 xhr 的回调函数封装成任务并添加到消息队列中,等主线程循环系统执行到该任务的时候,就会根据相关的状态来调用对应的回调函数

  • 如果网络请求出错了,就会执行 xhr.onerror
  • 如果超时了,就会执行 xhr.ontimeout
  • 如果是正常的数据接收,就会执行 onreadystatechange 来反馈相应的状态

XMLHttpRequset使用过程的坑

  • 跨域问题

    在极客邦的官网使用 XMLHttpRequest 请求极客时间的页面内容

    由于极客邦的官网是www.geekbang.org,极客时间的官网是time.geekbang.org,不是同一个源,所以就涉及到了跨域

    默认情况下,跨域请求是不被允许的

  • HTTPS混合内容的问题

    HTTPS 混合内容是 HTTPS 页面中包含了不符合 HTTPS 安全要求的内容,比如包含了 HTTP 资源,通过 HTTP 加载的图像、视频、样式表、脚本等,都属于混合内容

    通常,如果 HTTPS 请求页面中使用混合内容,浏览器会针对 HTTPS 混合内容显示警告,用来向用户表明此 HTTPS 页面包含不安全的资源

    但是通过 HTML 文件加载的混合资源,虽然给出警告,但大部分类型还是能加载的

    而使用 XMLHttpRequest 请求时,浏览器认为这种请求可能是攻击者发起的,会阻止此类危险的请求