mengtuifrontend / Blog

芦叶满汀洲,寒沙带浅流。二十年重过南楼。柳下系船犹未稳,能几日,又中秋。 黄鹤断矶头,故人今在否?旧江山浑是新愁。欲买桂花同载酒,终不似,少年游。
18 stars 5 forks source link

XMLHttpRequest #22

Open shenxuxiang opened 5 years ago

shenxuxiang commented 5 years ago

XMLHttpRequest

浏览器在 XMLHttpRequest 类上定义了他们的 HTTP API。这个类的每一个实例都表示一个独立的请求/响应,并且这个对象的属性和方法允许指定请求细节和提取响应数据。W3C在 XMLHttpRequest 规范的基础上制定了2级 XMLHttpRequest (下文简称 XHR2 )标准草案,且大部分浏览器都已经支持了。

在介绍 XMLHttpRequest 之前,我想先简单说一下HTTP请求组成部分和响应的组成部分

一个 HTTP 请求由4部分组成:

服务器返回的 HTTP 响应包含3部分:

HTTP 请求的各部分有指定的顺序:请求方法和URL首先到达,然后是请求头,最后是主体。XHMLHttpRequest 实现通常直到调用send()方法才开始启动网络。但 XMLHttpRequest API的设计似乎使每个方法都将写入网络流。这意味着调用 XMLHttpRequest 上的方法的顺序必须匹配 HTTP 请求的架构。例如,setRequestHeader() 方法的调用必须在调用 open() 之前但是在 send() 之后,否则就会跑出异常。下面我门会按照这个顺序来介绍。

实例化

使用 XMLHttpRequest API的第一件事就是先实例化;

  var request = new XMLHttpRequest();

我们也能重用已经存在的 XMLHttpRequest对象,但是这将会中止之前通过该对象发起的任何请求,但一般不会这么用

指定请求

  request.open('POST', url);

设置请求头

  request.setRequestheader('Content-Type': 'text/plain');

注意:

发送请求

使用 XMLHttpRequest 发起HTTP请求的最后一步就是指定可选的请求主体并向服务器发送它:

  request.send(null);

XMLHttpRequest 属性

readystatechange

XMLHttpRequest 对象通常异步使用:发送请求后,send() 方法立即返回,直到响应返回。为了在响应准备就绪的时候得到通知,必须监听 XMLHttpRequest 对象上的 readystatechange 事件。

readyState

它是一个整数, 他指定了 HTTP 请求的状态。

status

服务器返回的http状态码,当 readyState 小于 3 的时候读取这一属性会导致一个异常。

statusText

以数字和文字的形式返回 HTTP 状态码。

getRequestHeader()/getAllRequestHeaders()

使用这两个方法都可以查询到响应头。XMLHttpRequest 会自动处理 cookie,他会从 getAllRequestHeaders() 头返回集中过滤掉 cookie 头。而如果给 getRequestHeader() 传递 Set-CookieSet-Cookie2 则会返回 null

responseText

responseText 接受到服务器的相应数据 返回的值是一个json字符串 通过 JSON.parse(xhr.responseText) 可以得到数据对象

responseXML

responseXML属性可以得到 XML 的 Document形式的数据。

XHR2新增的事件集

XHR2规范定义了很多有用的事件集,在这个新的事件模型中,XMLHttpRequest 对象在请求的不同阶段触发不同类型的事件,所以它不再需要检查 readyState 属性
。

注意: 上面的这些事件我们可以通过 xhr.addEventListener() 方法进行监听。

progress事件中有三个属性需要讲解一下:

  xhr.onprogress = function(event) {
    if (event.lengthComputable) {
            
      const progress = event.loaded / event.total * 100;
    }
  }

XHR2 新增的 upload 属性

XHR2 中新增了一个 upload 属性,这个属性值是一个对象,他定义了 addEventListener() 和 整个 progress 事件集合,比如说 onprogressonload。(但 upload 没有定义 onreadystatechange 属性,upload 仅能触发新的事件类型)。

注意

upload 属性上定义的事件主要用在上传文件时。我们可以使用 upload 上的 onloadstart,onprogress 分别监听文件开始上传和上传过程中进度的变化。

对于文件上传,我们如何设置请求头??

  const input = document.getElementsByTagName('input')[0];
  input.addEventListener('change', function() {
    var file = this.files[0];
    if (!file) return;
    var xhr = new XMLHttpRequest();
    xhr.open('POST', url);
    xhr.send(file);
  }, false);

文件类型是更普通的二进制大对象 Blob 类型中的一个字类型。XHR2 允许向 send() 方法传入任何 Blob 对象。如果没有显示的设置 Content-Type 头,这个 Blob 对象的 type 属性用于设置待上传的 Content-Type 头。

multipart/form-data请求

XHR2 定义了新的 FormData API, 它容易实现多部分请求主体。首先,使用 FormData() 构造函数创建 FormData 对象,然后按需多次调用这个对象的 append() 方法把个体部分(string/File/Blob对象) 添加到请求中。最后把 FormData 对象传递给 send() 方法。send() 方法将对请求定义合适的边界字符串和设置 Content-Type 头。

中止请求和超时

中止请求

通过调用 XMLHttpRequest 对象的 abort() 方法来取消正在进行的 HTTP 请求。当调用 abort() 方法后会触发 xhr 对象的 onabort 事件。

超时

XHR2 定义了 timeout 属性来指定请求自动终止的毫秒数。同时也定义了 timeout 事件,当超时发生时触发。

demo

  // 封装一个request方法
  const request = (url, formData, cb) => {
    // 初始化
    const xhr = new XMLHttpRequest();
    // 定义请求的方法/动作和url
    xhr.open('POST', url);
    // 设置超时时间,单位是毫秒
    xhr.timeout = 2000; 

    xhr.ontimeout = function() {
      console.log('timeout');
    };
    // 开始上传
    xhr.upload.onloadstart = function() {
      console.log('开始上传');
    };
    // 上传的进度
    xhr.upload.onprogress = function(event) {
      // 只有当 lengthComputable 为true是,loaded 才有值
      if (event.lengthComputable) {
        const value = Math.ceil((event.loaded / event.total) * 100);
        cb && cb({
          status: 'loading',
          progress: value,
          data: null,
        });
      }
    };
    // 监听事件完成, 完成并不一定代表请求成功,所以需要判断 status 状态码
    xhr.onload = function() {
      const resp: Response = {
        status: 'success',
        progress: 100,
        data: null,
      };
      if (xhr.status === 200) {
        resp.data = JSON.parse(xhr.responseText);
        cb && cb(resp);
      } else {
        resp.status = 'error';
        cb && cb(resp);
      }
    };
    xhr.onerror = function() {
      cb && cb({
        status: 'error',
        progress: 0,
        data: null,
      });
    };
    xhr.onabort = function() {
      console.log('onabort');
    };
    xhr.onloadend = function() {
      console.log('上传结束');
    };
    xhr.send(formData);
    return xhr;
  };

HTTP跨域请求

作为同源策略的一部分,XMLHttpRequest 对象通常仅可以发起和文档具有相同服务器的 HTTP 请求。这个限制关闭了安全里漏洞,但同时也阻止了大量可使用的跨域请求。好在 XHR2 通过在 HTTP 响应中选择发送合适的 CORS(Cross-Origin Resource Sharing,跨域资源共享) 允许跨域访问网站。在日常开发中使用跨域请求并不需要进行的额外的其他设置,只要浏览器支持 CORS 跨域请求就行。 虽然实现 CORS 支持跨域的请求工作不需要做任务的事情,但有一些安全细节需要了解:

注意:XMLHttpRequest 的跨域请求同样包含简单请求和非简单请求,非简单请求又会进行预检请求,具体 CORS 的相关知识可以查看之前的分享的文章点击这里

总结

XMLHttpRequest API非常的好用,而且目前市面上的主浏览器也基本上都支持。相比 fetch 而言,兼容性肯定是更胜一筹,唯一不足的是不支持 Promise,但是这也难不倒我们程序员,自己封装一层就可以了。更为重要的是 XMLHttpRequest 支持超时设置和中止请求,还有进度事件,这些都是 'fetch' 所不具备的。

参考