高级前端工程师的进阶之路 --- 数据请求篇

数据请求知多少

老铁们都知道上网是通过浏览器发送HTTP请求,接着服务器后收到请求,发送资源到浏览器,然后浏览器解析静态资源(如html,css,js脚本)来展示给用户。(若对HTTP感兴趣的,下期会出一个高级前端进阶 ---http透析篇)

那前端开发人员在开发中,现有的几种网络请求数据的方法又有哪些呢?它们又有什么不同呢?

下面,我们将聊聊数据请求这个话题!let's go

数据请求之Ajax

ajax全称是Asynchronous Javascript and XML (异步的JavaScript和XML)最早出现的发送后端请求数据的技术,一种在无需重新加载整个网页的情况下,能够更新部分网页的技术,属于原生的js手段,核心的使用的是XMLHttpRequest对象,多个请求如果之间有先后顺序的话, 就会出现烦人的callback hell。

数据请求之jQuery Ajax

jQuery Ajax是小胖子John Resig用jQuery对ajax进行底层的封装。简单的实现有:.post等。

$.ajax() 返回的是其内部创建的XMLHttpRequest对象。一般不会使用这个这个函数,除非需要更多的选择。

数据请求之Axios

axios是需要安装使用的,支持浏览器客户端,也支持nodejs端使用。不是原生JS,是一个基于promise的HTTP库。它的特性是:在浏览器中创建XMLHttpRequest,在node.js中创建http请求,支持所有的promise的API,拦截请求和响应,转化请求数据和响应数据,取消请求,自动转换JSON数据,客户端支持防御XSRF。

什么是xsrf攻击呢?

全称是Cross Site Request Forgery,跨站域请求伪造,是网络攻击的一种,关于网络攻击以后会出一篇详细文章。那这里axios是如何通过客户端来防御xsrf呢? 这里让你的每个请求都带一个从cookie中拿到的key,根据浏览器的同源策略,伪造的请求是拿不到你cookie的key的,这样,后台就可以轻松辨别这个请求是否是用户在假冒网站上的误导输入了,采用正确的策略进行回应。

数据请求之Fetch

Fetch是一个现代的概念,等同于XMLHttpRequest。提供了许多与XMLHttpRequest相同的功能,但被设计的更有扩展性和高效性,核心在于对HTTP接口的抽象,包括Request,Response,Headers,Body,以及用于初始化异步请求的global fetch。使fetch可以被使用到更多的应用场景中:无论是service worker,Cache API ,甚至是需要你自己在程序中生成响应的方式。fetch也是基于promise设计的。且代码结构比ajax简单多了,参数和jQuery ajax相似。但是,fetch不是对ajax的进行底层封装,他就是原生的js

数据请求详细说

由浅入深Ajax

通过使用ajax技术,通过在后台与服务器进行少量的数据交换,就可以使页面进行异步更新。这样做的好处是,可以在不用加载整个网页的情况下,对网页的某个部分进行更新。

Ajax的工作原理

浏览器端:发生某个事件,创建XMLHttpRequest对象,发送HttpRequest ---> Internet ---> 在服务器端:处理HttpRequest 创建响应并将数据返回浏览器 ----> Internet ---> 在浏览器端: 使用js处理返回的数据,显示到页面上

  • 什么是XMLHttpRequest?

XMLRequest让发送HTTP请求变得非常的容易。只需要简单的创建一个本实例对象,打开一个url,然后发送这个请求。当传输完毕后,结果的HTTP状态,以及返回的响应内容也可以从请求对象中获取。实例代码:

function reqListener() {
    console.log(this.responseText);
}
let oReq = new XMLHttpRequest();
oReq.addEventListener("load", reqListener); //传输完毕,加载的数据保存在response中 reqListener是回调函数   
oReq.open('GET','http://www.example.org/example.txt',true); //第三个参数 true 意味着是异步请求
oReq.send();
  • 请求类型

通过XMLHttpRequest生成的请求可以有两种方式来获取数据,异步模式和同步模式。请求的类型由上述的实例代码中的open() 方法的第三个参数async的值来决定的。如果该参数是false的话,则该XMLHttpRequest请求以同步模式进行,否则该过程将以异步模式完成。(在部分新版本的浏览器中,主线程上的同步请求已经被放弃了)

  • 处理响应

w3c规范定义了XMLHttpRequest对象的几种类型的响应属性。这些属性告诉客户端关于XMLHttpRequest返回状态的重要信息。一些处理非文本返回类型的用例可能包含一些下面章节中描述的操作和分析。

分析操作responseXML属性:

它是一个只读值,它返回一个包含请求检索的HTML或XML的Document,如果请求未成功,尚未发送,或者检索的数据无法正确解析为 XML 或 HTML,则为 null。默认是当作“text / xml” 来解析。当 responseType 设置为 “document” 并且请求已异步执行时,响应将被当作 “text / html” 来解析。responseXML 对于任何其他类型的数据以及 data: URLs 为 null。

如果使用XMLHttpRequest来获得一个远程的XML文档的内容,responseXML属性将会是一个由XML文档解析而来的DOM对象,这很难被操作和分析。以下是五种主要的分析XML文档的方式:

1,使用XPath定位到文档的指定部分。

2,手工的解析和序列化XML为字符串或对象。

3,使用XMLSerializer把DOM树序列化成字符串或文件。

4,如果预先知道XML文档的内容,你可以使用RegExp。如果你用RegExp扫描时受到换行符的影响,你有可能想要删除所有的换行符。然而,这种方法是“最后手段”,因为如果XML代码发生了轻微的变化,该方法将可能失败。

解析和操作包含HTML文档的responseText属性:

如果使用XMLHttpRequest从服务端获取到一个HTML页面,则所有HTML标记会以字符串的形式存放在responseText属性里,这样就使得操作和解析这些HTML标记变得非常的困难。解析这些标记主要会有以下的三种方式:

1,XMLHttpRequestresponseXML属性

2,将内容通过fragment.body.innerHTML注入到一个文档片段中,并遍历DOM中的片段。

3,如果预先知道html文档的内容,可以使用RegExp。如果你用RegExp扫描时受到换行符的影响,你有可能想要删除所有的换行符。然而,这种方法是“最后手段”,因为如果HTML代码发生了轻微的变化,该方法将可能失败。

  • 处理二进制数据

有时通过XMLHttpRequest也可以发送和接受二进制内容,有很多方法可以强制使用XMLHttpRequest发送二进制数据,利用XMLHttpRequest.overrideMimeType()方法是一个解决方案,虽然它并不是一个标准方法。

let oReq = new XMLHttpRequest();
oReq.open("GET",url,true);
oReq.overrideMimeType("text/plain;")

在XMLHttpRequest Level2 规范中新加入了responseType属性,使用二进制通信变得更加容易和便捷。

let oReq = new XMLHttpRequest();
oReq.onload = function(e) {
    let arraybuffer = oReq.response; //not responseText
}
oReq.open("GET",url,true)
oReq.responseType = "arraybuffer" //在send之前修改数据类型 变为二进制类型
oReq.send()
  • 事件监测

XMLHttpRequest 提供了各种在请求被处理期间发生的事件以供监听(如定期进度通知,错误处理等)

支持DOM的progress事件 监测于XMLHttpRequest传输,遵循web API 进度事件规范:这些事件实现了ProgressEvent接口。

  • 提交表单和上传文件

XMLHttpRequest的实例有两种方式来提交表单和上传文件:

1, 使用Ajax

2,使用FormData API

使用formdata api 是最简单最快捷的,但是被收集的数据无法使用JSON.stringify() 转换为一个JSON字符串。

第一种方式是最复杂的但是是最灵活的和最强大的。

由浅入深 jQuery Ajax

实例:

$.ajax({
    url: "/api/getWeather",
    data: {
        zipcode: 97201
    },
    success: function(result) {
        $("#weather-temp").html("

"+result+"

") } })

第一个的Ajax指的是XMLHttpRequest(xhr),最早出现的发送后端请求,隶属于原始的js,核心使用的是XMLHttpRequest对象,多个请求之间如果有先后关系的话,会有callback hell 出现。

jQuery Ajax的出现是对原生xhr的封装,除此之外还增添了对JSONP(向后端发送一个请求,得到的是一个函数的调用,在前端页面上挂载同名函数)的支持,jQuery ajax的出现,很大程度上解决了数据请求的普遍问题,但是随着现行的react,vue等框架的兴起和大批的使用,以及ES规范的完善,更多API的更新,使jQuery ajax 开始暴露出弱点:

1,本身是怎对MVC的编程,不符合现在的MVVM框架的使用,配置和调用方式非常的混乱,基于事件的异步模型不太友好。

2,基于原生的XHR开发,XHR自己的架构都不清晰,也有了Fetch的替代方案的提出。

3,jQuery整个项目太大,单纯使用ajax要引入整个jQuery,这样非常不明智。

默认的情况下,ajax请求使用的GET方法。如果要使用POST方法,可以设定Type参数值。这个选项也会影响data选项中的内容如何发送到服务器(是在地址栏中还是请求体中)。

data选项既可以是一个查询字符串,比如key1=value1&key2=value2,也可以是一个映射,比如{ key1: 'value1',key2: 'value2' }。如果使用了映射的形式,则数据再被发送会被转换成查询字符串。这个处理方式可以通过processData选项为false来回避。若是要发送一个XML对象给服务器时,这种并不合理。我们也应当改变contentType选项的值,用其他合适的MIME类型来取代默认的application/x-www-form-urlencoded。

var list = {}
$.ajax({
    //请求方式
    type: "POST",
    //请求类型
    contentType: "application/json;charset=UTF-8",
    //请求地址
    url: "http://127.0.0.1/xxx/",
    //数据,json字符串.
    data: JSON.stringify(list),
    //请求成功
    success: function(result) {
    console.log(rusult)
    },
    error: function(e) {
        console.log(e.status);
        console.log(e.responseText)
    }
})
方法 描述
$.ajax() 执行异步AJAX请求
$.ajaxPrefilter() 每个请求发送之前且被$.ajax()处理之前,处理自定义Aajx选项或修改已存在选项
$.ajaxSetup() 为将来的AJAX请求设置默认值
$.get() 使用AJAX的HTTP GET请求从服务器加载数据
$.getJSON() 使用AJAX的HTTP GET 请求从服务器加载JSON编码的数据
$.getScript() 使用AJAX的HTTP GET 请求从服务器加载并执行JavaScript
$.param() 创建数组或对象的序列化表示形式(可用于AJAX请求的URL查询字符串)
$.post() 使用AJAX的HTTP POST请求
ajaxComplete() 规定AJAX请求完成时运行的函数
ajaxError() 规定AJAX请求失败时运行的函数
ajaxSend() 规定AJAX请求发送之前时运行的函数
ajaxStart() 规定第一个AJAX请求发送之前运行的函数
ajaxStop() 规定所有的AJAX请求完成时运行的函数
ajaxSuccess() 规定AJAX请求成功时运行的函数
load() 从服务器加载数据,并把返回的数据放置到指定的元素中
serialize() 编码表单元素集为字符串以便提交
由浅入深Axios

实例:

//get 请求
axios.get('/user?ID=12345')
    .then(function(response) {
    console.log(response)
})
    .catch(function(error) {
    console.log(error)
})
//另一种写法
axios.get('/user',{
    params: {
        ID: 12345
    }
})
    .then(function (response) {
    console.log(response)
})
    .catch(function (error) {
    console.log(error)
})
//post 请求
axios.post('/users',{
    firstName: 'Fred',
    lastName: 'Flintstone'
})
    .then(function(response) {
    console.log(response)
})
    .catch(function(error) {
    console.log(error)
})
// 执行多个并发请求
function getUserAccount() {
    return axios.get('/user/1234')
}
function getUserPermissions() {
    return axios.get('/user/1234/permissions')
}
axios.all([getUserAccount(),getUserPermissions()])
    .then(axios.spread(function (acct,perms) {
    //请求都执行完毕后
}))

在知多少中,我们已经提到,Axios本质上是对原生XHR的封装,只不过是通过Promise的实现版本,可以用在浏览器端和node.js中,符合最新的ES规范。

用axios创建请求的时候可以用的配置选项,只有url是必需的。如果没有指定的method,请求将默认使用get方法。

axios既提供了并发的封装,体积也较小。也没有fetch的一些获取问题。目前来看,这也是尤玉溪在vue中推崇axios的原因。

由浅入深Fetch

Fetch是一个现代的概念,等同于XMLHttpRequest。提供了许多与XMLHttpRequest相同的功能,但被设计的更有扩展性和高效性.

核心在于对HTTP接口的抽象,包括Request,Response,Headers,Body,以及用于初始化异步请求的global fetch。

也提供了一个全局的fetch() 方法,该方法提供了一种简单,合理的方式来跨网络异步获取资源。

请注意,fetch规范和jQuery ajax() 有两种方式的不同:

  • 当接收的一个错误的HTTP状态码(404),从fetch() 返回的是Promise 不会被标记为reject,即便该HTTP响应的状态码是500。它会将promise状态标记为resolve(但是会将resolve的返回值得ok属性设置为false),仅当网络故障或请求被阻止时,才会标记为reject。
  • 默认情况下,fetch不会从服务端发送或接收任何cookies,如果站点依赖于用户session,则会导致未经认证的请求(要发送cookies,必须设置credentials选项)

实例:

fetch('http://example.com/....json')
    .then(function(response) {
    return response.json();
})
    .then(function(myJson) {
    console.log(myJson)
})

这里我们可以通过网络获取一个JSON文件并将其打印到控制台。最简单的用法是只提供一个参数用来指明想fetch() 到的资源路径,然后返回一个包含响应结果的promise(一个Response对象)

当然她只是一个HTTP响应,不是真正的json。为了获取JSON的内容,我们需要使用json() 方法(在Bodymixin中定义,被 Request和Response对象实现)

fetch() 接受第二个可选参数,一个可以控制不同配置的init对象:

function postData(url,data) {
    return fetch(url,{
        body: JSON.stringify(data),
        cache: 'no-cache',
        credentials: 'same-origin', //include
        headers: {
            'user-agent': 'Mozilla/4.0 MDN Example',
            'content-type': 'application/json'
        },
        method: 'POST',  // GET POST DELETE PUT
        mode: 'cors', // no-cors cors same-origin
        redirect: 'follow',
        referrer: 'no-referrer', 
    })
    .then(response=>response.json())  //转化为json
}
postData('http://example.com/answer',{answer: 42})
.then(data=>console.log(data))
.catch(error=>console.error(error))

fetch的优点:

1,语法更加简洁

2,基于标准Promise实现,支持async/await

3,更加底层,使用的API更加丰富(request,response)

4,脱离了xhr,是ES规范里新的实现方式

5,跨域的请求处理

在设置了跨域头或借助了JSONP的前提下,浏览器的请求是可能随便跨域,但是,fetch中可以设置mode来进行是否跨域。

fetch的mode配置项有3个值,如下:

  • same-origin:该模式是不允许跨域的,它需要遵守同源策略,否则浏览器会返回一个error告知不能跨域;其对应的response type为basic。意味值请求必须是同源下。

  • cors: 该模式支持跨域请求,顾名思义它是以CORS的形式跨域;当然该模式也可以同域请求不需要后端额外的CORS支持;其对应的response type为cors。 通过cors方式跨域,后台配合。

  • no-cors: 该模式用于跨域请求但是服务器不带CORS响应头,也就是服务端不支持CORS;这也是fetch的特殊跨域请求方式;其对应的response type为opaque

针对跨域请求,cors模式是常见跨域请求实现,但是fetch自带的no-cors跨域请求模式则较为陌生,该模式有一个比较明显的特点:

该模式允许浏览器发送本次跨域请求,但是不能访问响应返回的内容,这也是其response type为opaque透明的原因。

这与发送的请求类似(通过src请求到一个资源,也可以通过fetch来请求image),该模式不能访问响应的内容信息;但是它可以被其他APIs进行处理,例如ServiceWorker。另外,该模式返回的repsonse可以在Cache API中被存储起来以便后续的对它的使用,这点对script、css和图片的CDN资源是非常合适的。

总得来说,fetch在前端跨域的处理上功能非常强大,原生xhr可望不可及。

参考资料: axios官网 / fetch mdn

参考公众号文章:几种数据请求的介绍,区别及优缺点 --- 全栈者

你可能感兴趣的:(高级前端工程师的进阶之路 --- 数据请求篇)