网络请求发展介绍

596 阅读5分钟

本文讲解浏览器请求的发展历程,从传统web应用到ajax,再到jquery,直到目前最为流行的axios。(重点详细讲,没用过的写个思想)

一、传统web应用

传统的网页(不使用 AJAX)如果需要更新内容,必需重载整个网页。过程是用户在客户端触发HTTP请求,服务器处理请求并返回新的HTHL页到客户端,客户端再重新读取整个页面。即使很小的交互,也要完成上述过程,导致浪费带宽且交互响应很慢。

在这个背景下,出现了可以更新部分网页数据的Ajax技术。

二、Ajax

1. 概念

AJAX即 Asynchronous JavaScript and XML。它不是新的编程语言,而是在不重新加载整个页面的情况下,与服务端交换数据并更新部分网页的技术。

2. 原理

Ajax 的工作原理相当于在用户和服务器之间加了—个中间层,使用户操作与服务器响应异步化,并且确定需要从服务器读取新数据时再由Ajax引擎代为向服务器提交请求,像—些数据验证和数据处理等都交给Ajax引擎自己来做,并不提交给服务端,提升性能。

3. 简单示例

Ajax 核心由 XMLHTTPRequest、JavaScript、DOM 对象组成,通过XmlHttpRequest对象来向服务器发异步请求,从服务器获得数据,然后用 JavaScript 来操作 DOM 而更新页面。

const xmlHttp = new XMLHttpRequest()
// 发送请求
xmlHttp.open('GET', 'text1.txt', true) // open(method,url,async)
xmlHttp.send()

// 响应处理
xmlHttp.onreadystatechange = function () {
  // 请求已完成,且响应就绪
  if (xmlHttp.readyState === 4  && xmlHttp.status === 200) {
    document.getElementById('div1').innerHTML = xmlHttp.responsText
  }
}

4. XMLHttpRequest对象

  • readyState:存有 XMLHttpRequest 的状态。从 0 到 4 发生变化。改变时触发onreadystatechange
    • 0: 请求未初始化
    • 1: 服务器连接已建立
    • 2: 请求已接收
    • 3: 请求处理中
    • 4: 请求已完成,且响应已就绪
  • status: 服务器的HTTP状态码
    • 200: "OK"
    • 404: 未找到页面
    • ...

5. 优缺点

  • 优点:

    • 无刷新更新数据,用户体验提高
    • 按需取数据,节约空间和带宽
    • 部分工作转嫁到客户端,减轻服务器负担
    • 基于标准被支持
  • 缺点:

    • 客户端变的复杂,容易出错
    • 移动设备支持较差
    • 安全问题

三、Jquery

1. 背景

因为不同的浏览器对 AJAX 的实现并不相同,所以编写统一的 AJAX 代码相对复杂。例如IE5、IE6没有XMLHttpRequest对象,而是ActiveXObject。

Jqury 库对 AJAX 进行了封装,使得我们可以通过更为简单统一的方式实现 AJAX 功能,同时能够把这些外部数据直接载入网页的被选元素中。

2. 主要 API 介绍

  • $(selector).load(URL, data, callback): 从服务端获取获取并将数据载入到DOM元素中
  • $.get(URL,callback) : 使用 GET 方法从服务端获取数据
  • $.post( url [, data ] [, success ] [, dataType ] ) : 通过 HTTP POST 请求从服务器上请求数据。
  • api详情

四、Fetch

1. Why fetch

传统ajax(指的是 XmlHttpRequest,即XHR)已经逐渐被Fetch替代。原因:

  • Fetch语法简单,更加语义化, 并能方便的配置请求对象。
  • 不需要引入jquery,且移动端支持较好

2. 简单示例

fetch(url).then(function(response) {
  return response.json();
}).then(function(data) {
  console.log(data);
}).catch(function(e) {
  console.log("Oops, error");
});

// 使用 ES6 的 箭头函数 后:
fetch(url).then(response => response.json())
  .then(data => console.log(data))
  .catch(e => console.log("Oops, error", e))

3. polyfill: whatwg-fetch

fetch的原生支持并不好,通过引入polyfill来支持主流浏览器。实质是对于不支持 fetch api 的浏览器,使用 XMLHttpRequest 模拟实现 fetch api。

  • 关键源码:

    if (self.fetch) {
      return
    }
    ...
    self.fetch = function(input, init) {
        return new Promise(function(resolve, reject) {
          var request = new Request(input, init)
          var xhr = new XMLHttpRequest()
    
          xhr.onload = function() {...}
        }
    }
    
  • plato((项目采用的vue脚手架)引入方法:

    // src/polyfills/index
    import 'whatwg-fetch'
    
    // webpack.config.bable.js
    entry: {
      app: [
        paths.src('polyfills/index.js'),  // 加载 polyfills
        paths.src('index.js')]  // 加载入口
    }
    
  • 和Jquery.ajax()的区别

    • 服务器返回 404,500 错误码时并不会reject,只有网络错误或者其他原因导致请求不能完成时,fetch 才会被 reject。为了保证reject HTTP错误状态,可做如下处理:eg:所有非2xx的错误码都抛出错误异常。 (----- 不好用,有时候需要做处理)

      function checkStatus(response) {
        if (response.status >= 200 && response.status < 300) {
          return response
        } else {
          var error = new Error(response.statusText)
          error.response = response
          throw error
        }
      }
      
      function parseJSON(response) {
        return response.json()
      }
      
      fetch('/users')
        .then(checkStatus)
        .then(parseJSON)
        .then(function(data) {
          console.log('request succeeded with JSON response', data)
        }).catch(function(error) {
          console.log('request failed', error)
        })
      
    • plato(项目采用的vue脚手架)实现建request.js

      const body = await getBody(res)
      if (res.status >= 200 && res.status < 400) {
        return body
      } else {
        throw body
      }
      
      async function getBody (res) {
        const type = res.headers.get('Content-Type')
      
        if (type && type.indexOf('json') !== -1) {
          return await res.json()
        }
      
        const body = await res.text()
      
        try {
          return JSON.parse(body)
        } catch (error) {
          return { body }
        }
      }
      
  • Fetch 请求默认是不带 cookie 的,而且也不接收cookie。

    • 发送cookie

      • 不跨域:fetch(url, {credentials: 'same-origin'})
      • CORS 跨域请求:fetch(url, {credentials: 'include'})
    • 接收cookie
      同XHR相同, 从服务端返回的响应头Set-Cookie是一个被禁的字段,所以不能通过response.headers.get()来获取。获取cookie应该是浏览器的职责,通过document.cookie来实现。

4. fetch() 详解

  • 语法: Promise fetch(input[, init])
  • 参数
    • input:定义要获取的资源
    • init: 一个配置项对象,包括所有对请求的设置。可选的参数有:
      • method
      • headers
      • body
      • mode
      • credientials
      • cache
      • ...
  • 返回值: 一个 Promise,resolve 时回传 Response 对象。

五、Axios

基于Promise的HTTP库,用于浏览器端和Node.js端, 底层实现也是封装XMLHttpRequest。

  • Why axios:

    • 支持两端:browser, nodejs
    • 支持拦截器
    • 支持json数据自动转换
    • 基于promise,使用简单
    • 体积小
    • fetch 实现偏底层,通常需要封装后使用,axios可直接用
  • 特点

    • 浏览器端:make XMLHttpRequest
    • Nodejs:make http request
    • 支持promise api
    • 支持请求 intercept、响应intercept
    • 自动转换JSON数据

参考:

五、参考