Ajax 异步数据传输

11/1/2020 ajax

# HTTP 响应协议

# HTTP 概念

HTTP是一种能够获取如 HTML 这样的网络资源的 protocol (opens new window)(通讯协议)。它是在 Web 上进行数据交换的基础,是一种 client-server 协议,也就是说,请求通常是由像浏览器这样的接受方发起的。一个完整的Web文档通常是由不同的子文档拼接而成的,像是文本、布局描述、图片、视频、脚本等等。

# HTTP 请求交互的基本过程

请求流程

  1. 前后应用从浏览器端向服务器发送 HTTP 请求(请求报文)
  2. 后台服务器接收到请求后, 调度服务器应用处理请求, 向浏览器端返回 HTTP 响应(响应报文)
  3. 浏览器端接收到响应, 解析显示响应体/调用监视回调

# HTTP 请求报文

请求行: method url GET /product_detail?id=2 POST /login 多个请求头: Host: www.baidu.com Cookie: BAIDUID=AD3B0FA706E; BIDUPSID=AD3B0FA706; Content-Type: application/x-www-form-urlencoded 或者 application/json 请求体: username=tom&pwd=123 {"username": "tom", "pwd": 123}

# HTTP 响应报文

响应状态行: status statusText 多个响应头: Content-Type: text/html;charset=utf-8 Set-Cookie: BD_CK_SAM=1;path=/ 响应体: html 文本/json 文本/js/css/图片...

# post 请求体参数格式

Content-Type: application/x-www-form-urlencoded;charset=utf-8 用于键值对参数,参数的键值用=连接, 参数之间用&连接 例如: name=%E5%B0%8F%E6%98%8E&age=12

Content-Type: application/json;charset=utf-8 用于 json 字符串参数 例如: {"name": "%E5%B0%8F%E6%98%8E", "age": 12}

Content-Type: multipart/form-data 用于文件上传请求

# 常见的响应状态码

响应码 状态 状态详情
200 OK 请求成功。一般用于 GET 与 POST 请求
201 Created 已创建。成功请求并创建了新的资源
401 Unauthorized 未授权/请求要求用户的身份认证
404 Not Found 服务器无法根据客户端的请求找到资源
500 Internal Server Error 服务器内部错误,无法完成请求

# 不同类型的请求及其作用

  1. GET: 从服务器端读取数据
  2. POST: 向服务器端添加新数据
  3. PUT: 更新服务器端已经数据
  4. DELETE: 删除服务器端数据

# API 的分类

# REST API

发送请求进行 CRUD 哪个操作由请求方式来决定 同一个请求路径可以进行多个操作 请求方式会用到 GET/POST/PUT/DELETE

# 非 REST API

请求方式不决定请求的 CRUD 操作 一个请求路径只对应一个操作 一般只有 GET/POST

# json-server

JSON-Server 是一个 Node 模块,运行 Express 服务器,可以指定一个 json 文件作为 api 的数据源。

# 安装 json-server

npm install -g json-server
npm i cors express --save

# 启动 json-server

json-server可以直接把一个json文件托管成一个具备全RESTful风格的API,并支持跨域、jsonp、路由订制、数据快照保存等功能的 web 服务器。

db.json文件的内容:

{
  "course": [
    {
      "id": 1000,
      "course_name": "马连白米且",
      "autor": "袁明",
      "college": "金并即总变史",
      "category_Id": 2
    },
    {
      "id": 1001,
      "course_name": "公拉农题队始果动",
      "autor": "高丽",
      "college": "先了队叫及便",
      "category_Id": 2
    }
  }
}

例如以下命令,把db.json文件托管成一个 web 服务。

$ json-server --watch --port 53000 db.json

输出类似以下内容,说明启动成功。

/{^_^}/ hi!

Loading db.json
Done

Resources
http://localhost:53000/course

Home
http://localhost:53000

Type s + enter at any time to create a snapshot of the database
Watching...

此时,你可以打开你的浏览器,然后输入:http://localhost:53000/course

# 使用浏览器进行访问

http://localhost:53000/course/1000

# XMLHttpRequest的概述

XMLHttpRequest 最早是在IE5中以ActiveX组件的形式实现的。非 W3C 标准. 创建XMLHttpRequest对象(由于非标准所以实现方法不统一) Internet ExplorerXMLHttpRequest实现为一个ActiveX对象 其他浏览器(Firefox、Safari、Opera…)把它实现为一个本地的JavaScript对象。 XMLHttpRequest在不同浏览器上的实现是兼容的,所以可以用同样的方式访问XMLHttpRequest实例的属性和方法,而不论这个实例创建的方法是什么。

# Ajax工作原理

Ajax并不是一项新技术,它实际上是几种技术,每种技术各尽其职,以一种全新的方式聚合在一起 服务器端语言:服务器需要具备向浏览器发送特定信息的能力。Ajax与服务器端语言无关。 XML (eXtensible Markup Language,可扩展标记语言) 是一种描述数据的格式。Aajx 程序需要某种格式化的格式来在服务器和客户端之间传递信息,XML 是其中的一种选择 XHTML(eXtended Hypertext Markup Language),使用扩展超媒体标记语言)和 CSS(Cascading Style Sheet,级联样式单)标准化呈现; DOM(Document Object Model,文档对象模型)实现动态显示和交互; 使用XMLHTTP组件XMLHttpRequest对象进行异步数据读取 使用JavaScript绑定和处理所有数据

# Ajax的缺陷

AJAX不是完美的技术。使用AJAX,它的一些缺陷不得不权衡一下: 由 JavascriptAJAX 引擎导致的浏览器的兼容 页面局部刷新,导致后退等功能失效。 对流媒体的支持没有FLASHJava Applet好。 一些手持设备(如手机、PDA等)支持性差。

# 创建XMLHttpRequest对象

为了每次写Ajax的时候都节省一点时间,可以把对象检测的内容打包成一个可复用的函数:

function getHTTPObject(){
var xhr = false;
if(window.XMLHttpRequest){
	xhr = new XMLHttpRequest();
}else if(window.ActiveXObject){
	xhr = new ActiveXObject("Microsoft.XMLHTTP");
};return xhr;
}

[^说明]: 对window.XMLHttpRequest的调用会返回一个对象或null,if语句会把调用返回的结果看作是true或false**(如果返回对象则为true,返回null则为false)。**如果XMLHttpRequest对象存在,则把 xhr 的值设为该对象的新实例。如果不存在,就去检测 ActiveObject 的实例是否存在,如果答案是肯定的,则把微软 XMLHTTP 的新实例赋给 xhr

# XMLHttpRequest的方法

方法 描述
abort() 停止当前请求
getAllResponseHeaders() 把HTTP请求的所有响音首部作为键/值返回
open("method","url") 简历对服务器的调用。Method参数可以是GET或POST或PUT.url参数可以是相对url或绝对url
send(content) 向服务器发送请求
setRequestHeader("header","value") 把指定首部设置为所提供的值。在设置任何首部之前先调用open()

# XMLHttpRequest的属性

属性 描述
onreadystatechange 每个状态改变是都会触发这个时间处理器,通常会调用一个javaScript函数
readyState 请求的状态,有5个可取值:0=未初始化、1=正在加载、2=已经加载、3、交互中、4=、完成
responseText 服务器的响应,表示一个串
responseXML 服务器的响应,表示为XML,这个对象可以解析为DOM对象
status 服务器的HTTP状态码(200对应OK、404对应NotFount、等)
statusText HTTP状态码的相应文本(OK或NotFount等)

# 发送请求

利用XMLHttpRequest 实例与服务器进行通信包含以下3个关键部分: onreadystatechange 事件处理函数open 方法send 方法

# open(method, url, asynch)

XMLHttpRequest 对象的 open 方法允许程序员用一个Ajax调用向服务器发送请求。

method 请求类型,类似 “GET””POST”的字符串。若只想从服务器检索一个文件,而不需要发送任何数据,使用GET(可以在GET请求里通过附加在URL上的查询字符串来发送数据,不过数据大小限制为2000个字符)。若需要向服务器发送数据,用POST。 在某些情况下,有些浏览器会把多个XMLHttpRequest请求的结果缓存在同一个URL。如果对每个请求的响应不同,就会带来不好的结果。在此将时间戳追加到URL的最后,就能确保URL的唯一性,从而避免浏览器缓存结果。(“?time=”+new Date());

**url:**路径字符串,指向你所请求的服务器上的那个文件。可以是绝对路径或相对路径。

asynch 表示请求是否要异步传输,默认值为true。指定true,在读取后面的脚本之前,不需要等待服务器的相应。指定false,当脚本处理过程经过这点时,会停下来,一直等到Ajax请求执行完毕再继续执行。

# send(data)

open 方法定义了 Ajax 请求的一些细节。send 方法可为已经待命的请求发送指令 data:将要传递给服务器的字符串。 当向send()方法提供参数时,要确保open()中指定的方法是POST,如果没有数据作为请求体的一部分发送,则使用null.

var request = getHTTPObject()
request.open("GET","file.txt",true)
request.send(null)
request.onreadystatechange = doSomeThing

# onreadystatechange

该事件处理函数由服务器触发,而不是用户

# 接收响应

用 XMLHttpRequest 的方法可向服务器发送请求。在 Ajax 处理过程中,XMLHttpRequest 的如下属性可被服务器更改: readyState status responseText responseXML

# readyState

readyState 属性表示Ajax请求的当前状态。它的值用数字代表。 0 代表未初始化。 还没有调用 open 方法 1 代表正在加载。 open 方法已被调用,但 send 方法还没有被调用 2 代表已加载完毕。send 已被调用。请求已经开始 3 代表交互中。服务器正在发送响应4 代表完成。响应发送完毕

每次 readyState 值的改变,都会触发 readystatechange 事件。如果把 onreadystatechange 事件处理函数赋给一个函数,那么每次 readyState 值的改变都会引发该函数的执行。 readyState 值的变化会因浏览器的不同而有所差异。但是,当请求结束的时候,每个浏览器都会把 readyState 的值统一设为 4

# status

服务器发送的每一个响应也都带有首部信息。三位数的状态码是服务器发送的响应中最重要的首部信息,并且属于超文本传输协议中的一部分。

常用状态码及其含义: 404 没找到页面(not found) 403 禁止访问(forbidden) 500 内部服务器出错(internal service error) 200 一切正常(ok) 304 没有被修改(not modified)

在 XMLHttpRequest 对象中,服务器发送的状态码都保存在 status 属性里。通过把这个值和 200 或 304 比较,可以确保服务器是否已发送了一个成功的响应

# responseText

XMLHttpRequest 的 responseText 属性包含了从服务器发送的数据。它是一个HTML,XML或普通文本,这取决于服务器发送的内容。

当 readyState 属性值变成 4 时, responseText 属性才可用,表明 Ajax 请求已经结束。

request.onreadystatechange = function(){
  if(request.readyState == 4){
		if(request.status == 200 || request.status == 304){
			alert(request.responseText)
		}
	}
}

# responseXML

如果服务器返回的是 XML, 那么数据将储存在 responseXML 属性中。 只用服务器发送了带有正确首部信息的数据时, responseXML 属性才是可用的。 MIME 类型必须为 text/xml

# 数据格式提要

在服务器端 AJAX 是一门与语言无关的技术。在业务逻辑层使用何种服务器端语言都可以。 从服务器端接收数据的时候,那些数据必须以浏览器能够理解的格式来发送。服务器端的编程语言只能以如下 3 种格式返回数据:- XML - JSON - HTML

# XML

**优点:**XML 是一种通用的数据格式。不必把数据强加到已定义好的格式中,而是要为数据自定义合适的标记。利用 DOM 可以完全掌控文档。 **缺点:**如果文档来自于服务器,就必须得保证文档含有正确的首部信息。若文档类型不正确,那么 responseXML 的值将是空的。当浏览器接收到长的 XML 文件后, DOM 解析可能会很复杂

# JSON

JSON(JavaScript Object Notation)一种简单的数据格式,比xml更轻巧。JSON是JavaScript原生格式,这意味着在JavaScript中处理JSON数据不需要任何特殊的API或工具包。 JSON的规则很简单:对象是一个无序的“‘名称/值’对”集合。一个对象以“{”(左括号)开始,“}”(右括号)结束。每个“名称”后跟一个“:”(冒号);“‘名称/值’对”之间使用“,”(逗号)分隔。

[{
	"songname": "70亿人の头の上に风船を",
	"singer": "きくお",
	"album": "きくおミク",
},{
	"songname": "70亿人の头の上に风船を",
	"singer": "きくお",
	"album": "きくおミク",
}]

JSON 用冒号(而不是等号)来赋值。每一条赋值语句用逗号分开。整个对象用大括号封装起来。可用大括号分级嵌套数据。

JSON 只是一种文本字符串。它被存储在 responseText 属性中 为了读取存储在 responseText 属性中的 JSON 数据,需要根据 JavaScript 的 eval 语句。函数 eval 会把一个字符串当作它的参数。然后这个字符串会被当作 JavaScript 代码来执行。因为 JSON 的字符串就是由 JavaScript 代码构成的,所以它本身是可执行的。

// 使用eval()
var jsonStr = xhr.responseText
var personObj = eval("("+jsonStr+")")
var name = personObj.name
// 使用JSON.parse()
var jsonStr = xhr.responseText
var personObj = JSON.parse(jsonStr)
var name = personObj.name

**优点:**作为一种数据传输格式,JSON 与 XML 很相似,但是它更加灵巧。JSON 不需要从服务器端发送含有特定内容类型的首部信息。 **缺点:**语法过于严谨,代码不易读,eval 函数存在风险

# HTML

HTML 由一些普通文本组成。如果服务器通过 XMLHttpRequest 发送 HTML, 文本将存储在 responseText 属性中。 不必从 responseText 属性中读取数据。它已经是希望的格式,可以直接将它插入到页面中。插入 HTML 代码最简单的方法是更新这个元素的 innerHTML 属性。 **优点:**从服务器端发送的 HTML 代码在浏览器端不需要用 JavaScript 进行解析。HTML 的可读性好。HTML 代码块与 innerHTML 属性搭配,效率高。 **缺点:**若需要通过 AJAX 更新一篇文档的多个部分,HTML 不合适,innerHTML 并非 DOM 标准。

# 对比小结

若应用程序不需要与其他应用程序共享数据的时候, 使用 HTML 片段来返回数据时最简单的 如果数据需要重用, JSON 文件是个不错的选择, 其在性能和文件大小方面有优势 当远程应用程序未知时, XML 文档是首选, 因为 XML 是 web 服务领域的 “世界语”

# 原生Ajax请求流程

# 封装兼容其他浏览器 getHTTPObject 对象

function getHTTPObject(){
var xhr = false;
if(window.XMLHttpRequest){
	xhr = new XMLHttpRequest();
}else if(window.ActiveXObject){
	xhr = new ActiveXObject("Microsoft.XMLHTTP");
}
return xhr;
}

# 原生 Ajax 发送请求流程

// 1. 创建 XmlHttpRequest 对象
var request = getHTTPObject()
// 2. 准备发送请求的数据:url
var url = 'helloAjax.txt'+"?time="+new Date(); // 防止缓存
var method = 'GET'
// 3. 调用 XMLHttpRequest 对象的 open 方法规定发送格式
request.open(method, url)
// 4. 调用 XMLHttpRequest 对象的 send 方法,发送请求
request.send(null)
// 5. 为 XMLHttpRequset 对象添加 onreadystatechange 监听函数
request.onreadystatechange = function () {
	// 6. 判断响应是否完成:XMLHttpRequest 对象的 readyState 属性值为4的时候
  // 7. 在判断响应是否可用:XMLHttpRequest 对象 status 属性值为 200 或者304
	if(request.readyState == 4 && request.status == 200 || request.status == 304){
		// 8. 打印输出结果 request
    	console.log(request)
	}
}

# jQyery-ajax 获取数据(框架)

# 返回结果替换元素文字

$dom.load(url);

# $.get()函数

$.get(url,data,success(response,status,xhr),dataType)

# $.post()函数

$.post(url,data,success(data, textStatus, jqXHR),dataType)

# $.ajax()函数

$.ajax({settings});

参数 类型 属性
url String 默认值: 当前页地址。发送请求的地址。
data String 发送到服务器的数据。将自动转换为请求字符串格式。GET 请求中将附加在 URL 后。查看 processData 选项说明以禁止此自动转换。必须为 Key/Value 格式。如果为数组,jQuery 将自动为不同值对应同一个名称。如 {foo:["bar1", "bar2"]} 转换为 '&foo=bar1&foo=bar2'。
success Function 请求成功后的回调函数。
dataType String 预期服务器返回的数据类型。如果不指定,jQuery 将自动根据 HTTP 包 MIME 信息来智能判断,比如 XML MIME 类型就被识别为 XML。在 1.4 中,JSON 就会生成一个 JavaScript 对象,而 script 则会执行这个脚本。随后服务器端返回的数据会根据这个值解析后,传递给回调函数。
type String 默认值: "GET")。请求方式 ("POST" 或 "GET"), 默认为 "GET"。注意:其它 HTTP 请求方法,如 PUT 和 DELETE 也可以使用,但仅部分浏览器支持。
timeout Number 设置请求超时时间(毫秒)。此设置将覆盖全局设置。
error Function 默认值: 自动判断 (xml 或 html)。请求失败时调用此函数。三个参数:XMLHttpRequest 对象、错误信息、(可选)捕获的异常对象。。
username String 用于响应 HTTP 访问认证请求的用户名。
password String 用于响应 HTTP 访问认证请求的密码
xhr Function 需要返回一个 XMLHttpRequest 对象。默认在 IE 下是 ActiveXObject 而其他情况下是 XMLHttpRequest 。用于重写或者提供一个增强的 XMLHttpRequest 对象。这个参数在 jQuery 1.3 以前不可用。
cache Boolean 默认值: true,dataType 为 script 和 jsonp 时默认为 false。设置为 false 将不缓存此页面。jQuery 1.2 新功能。
dataFilter Function 给 Ajax 返回的原始数据的进行预处理的函数。提供 data 和 type 两个参数:data 是 Ajax 返回的原始数据,type 是调用 jQuery.ajax 时提供的 dataType 参数。函数返回的值将由 jQuery 进一步处理
context Object 这个对象用于设置 Ajax 相关回调函数的上下文。也就是说,让回调函数内 this 指向这个对象(如果不设定这个参数,那么 this 就指向调用本次 AJAX 请求时传递的 options 参数)。比如指定一个 DOM 元素作为 context 参数,这样就设置了 success 回调函数的上下文为这个 DOM 元素。
jsonp String 在一个 jsonp 请求中重写回调函数的名字。这个值用来替代在 "callback=?" 这种 GET 或 POST 请求中 URL 参数里的 "callback" 部分,比如 {jsonp:'onJsonPLoad'} 会导致将 "onJsonPLoad=?" 传给服务器。
jsonpCallback String 为 jsonp 请求指定一个回调函数名。这个值将用来取代 jQuery 自动生成的随机函数名。这主要用来让 jQuery 生成度独特的函数名,这样管理请求更容易,也能方便地提供回调函数和错误处理。你也可以在想让浏览器缓存 GET 请求的时候,指定这个回调函数名。
contentType String 默认值: "application/x-www-form-urlencoded"。发送信息至服务器时内容编码类型。默认值适合大多数情况。如果你明确地传递了一个 content-type 给 $.ajax() 那么它必定会发送给服务器(即使没有数据要发送)。
processData Boolean 默认值: true。默认情况下,通过data选项传递进来的数据,如果是一个对象(技术上讲只要不是字符串),都会处理转化成一个查询字符串,以配合默认内容类型 "application/x-www-form-urlencoded"。如果要发送 DOM 树信息或其它不希望转换的信息,请设置为 false。
scriptCharset String 只有当请求时 dataType 为 "jsonp" 或 "script",并且 type 是 "GET" 才会用于强制修改 charset。通常只在本地和远程的内容编码不同时使用。
ifModified Boolean 仅在服务器数据改变时获取新数据。默认值: false。使用 HTTP 包 Last-Modified 头信息判断。在 jQuery 1.4 中,它也会检查服务器指定的 'etag' 来确定数据没有被修改过。
traditional Boolean 如果你想要用传统的方式来序列化数据,那么就设置为 true。请参考工具分类下面的 jQuery.param 方法。
async Boolean 默认值: true。默认设置下,所有请求均为异步请求。如果需要发送同步请求,请将此选项设置为 false。注意,同步请求将锁住浏览器,用户其它操作必须等待请求完成才可以执行。
complete(XHR, TS) Function 请求完成后回调函数 (请求成功或失败之后均调用)。参数: XMLHttpRequest 对象和一个描述请求类型的字符串。这是一个 Ajax 事件。
global Boolean 是否触发全局 AJAX 事件。默认值: true。设置为 false 将不会触发全局 AJAX 事件,如 ajaxStart 或 ajaxStop 可用于控制不同的 Ajax 事件。
beforeSend(XHR) Function 发送请求前可修改 XMLHttpRequest 对象的函数,如添加自定义 HTTP 头。XMLHttpRequest 对象是唯一的参数。这是一个 Ajax 事件。如果返回 false 可以取消本次 ajax 请求。

# axios-ajax 获取数据(框架)

Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。

# 执行 GET 请求

// 为给定 ID 的 user 创建请求
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('/user', {
    firstName: 'Fred',
    lastName: 'Flintstone'
  })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });

# 执行多个并发请求

function getUserAccount() {
  return axios.get('/user/12345');
}

function getUserPermissions() {
  return axios.get('/user/12345/permissions');
}

axios.all([getUserAccount(), getUserPermissions()])
  .then(axios.spread(function (acct, perms) {
    // 两个请求现在都执行完成
  }));

# fetch 获取数据(原生)

fetch('url').then(function(response) {return response.json();})
.then(function (data) {
    console.log(data);
})
 .catch(function (myJson) {
  	console.log(myJson)
});