Skip to main content

AJAX学习笔记

· 13 min read
kart jim

本文大部分摘抄自菜鸟教程,仅作学习使用。

AJAX简介

  • AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。
  • AJAX 不是新的编程语言,而是一种使用现有标准的新方法。
  • AJAX 最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容。
  • AJAX 不需要任何浏览器插件,但需要用户允许JavaScript在浏览器上执行。

ajax.png

ajax使用

ajax基本使用

ajax基本使用分为四步:

创建XMLHttpRequest对象

XMLHttpRequest 用于在后台与服务器交换数据。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。 所有现代浏览器都支持XMLHttpRequest对象。

var XHR = new XMLHttpRequest();

向服务器发送请求

如需将请求发送到服务器,需使用 XMLHttpRequest 对象的 open() 和 send() 方法:

XHR.open("GET","ajax_info.txt",true);
XHR.send();
方法作用
open(method,url,async)规定请求的类型、URL 以及是否异步处理请求。
method:请求的类型;GET 或 POST
url:文件在服务器上的位置
async:true(异步)或 false(同步)
send(string)将请求发送到服务器。
string:仅用于 POST 请求
get和post的选择

与 POST 相比,GET 更简单也更快,并且在大部分情况下都能用。
然而,在以下情况中,请使用 POST 请求:

  • 不愿使用缓存文件(更新服务器上的文件或数据库)
  • 向服务器发送大量数据(POST 没有数据量限制)
  • 发送包含未知字符的用户输入时,POST 比 GET 更稳定也更可靠

获取服务器响应

如需获得来自服务器的响应,请使用 XMLHttpRequest 对象的 responseText 或 responseXML 属性。

属性描述
responseText获得字符串形式的响应数据。
responseXML获得 XML 形式的响应数据。

您可以将获取的数据写入html:

document.querySelector(".box").innerText = XHR.responseText;

onreadystatechange 事件

当请求被发送到服务器时,我们需要执行一些基于响应的任务。
每当 readyState 改变时,就会触发 onreadystatechange 事件。
readyState 属性存有 XMLHttpRequest 的状态信息。

下面是 XMLHttpRequest 对象的三个重要的属性:

属性描述
onreadystatechange存储函数(或函数名),每当 readyState 属性改变时,就会调用该函数。
readyState存有 XMLHttpRequest 的状态。从 0 到 4 发生变化。
0: 请求未初始化
1: 服务器连接已建立
2: 请求已接收
3: 请求处理中
4: 请求已完成,且响应已就绪
status200: "OK"
404: 未找到页面

在 onreadystatechange 事件中,我们规定当服务器响应已做好被处理的准备时所执行的任务。
当 readyState 等于 4 且 status 为 200 时,表示响应已就绪;

我们可以将第三步的代码

document.querySelector(".box").innerText = XHR.responseText;

改一下:

const box = document.querySelector(".box");
XHR.onreadystatechange=function()
{
if (XHR.readyState==4 && XHR.status==200)
{
box.innerText = XHR.responseText;
}
}

注意: onreadystatechange 事件被触发 4 次(0 - 4), 分别是: 0-1、1-2、2-3、3-4,对应着 readyState 的每个变化。

到此基本的ajax已完成!

使用回调函数

回调函数是一种以参数形式传递给另一个函数的函数。

如果您的网站上存在多个 AJAX 任务,那么您应该为创建 XMLHttpRequest 对象编写一个标准的函数,并为每个 AJAX 任务调用该函数。

该函数调用应该包含 URL 以及发生 onreadystatechange 事件时执行的任务(例子来自菜鸟教程):

<!DOCTYPE html>
<html>
<head>
<script>
var xmlhttp;
function loadXMLDoc(url,cfunc){
if (window.XMLHttpRequest){
xmlhttp=new XMLHttpRequest();
}
else{// IE6, IE5 代码
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange=cfunc;
xmlhttp.open("GET",url,true);
xmlhttp.send();
}
function myFunction(){
loadXMLDoc("https://www.runoob.com/try/ajax/ajax_info.txt",function(){
if (xmlhttp.readyState==4 && xmlhttp.status==200){
document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
}
});
}
</script>
</head>
<body>

<div id="myDiv"><h2>使用 AJAX 修改文本内容</h2></div>
<button type="button" onclick="myFunction()">修改内容</button>

</body>
</html>

ASP/PHP

即在调用open()函数的时候,传入的url参数后面加上?q=,再加个变量:

str = 'a';
xmlhttp.open("GET",'gethint.php?q='+str,true);

AJAX 可用来与数据库进行动态通信

其实就是通过PHP文件与数据库进行交互。

使用 AJAX 来读取来自 XML 文件的信息

菜鸟教程上的例子:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
table,th,td {
border : 1px solid black;
border-collapse: collapse;
}
th,td {
padding: 5px;
}
</style>
</head>
<body>

<h1>XMLHttpRequest 对象</h1>

<button type="button" onclick="loadXMLDoc()">获取我收藏的 CD</button>

<table id="demo"></table>

<script>
function loadXMLDoc() {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
myFunction(this);
}
};
xhttp.open("GET", "cd_catalog.xml", true);
xhttp.send();
}
function myFunction(xml) {
var i;
var xmlDoc = xml.responseXML;
var table="<tr><th>Artist</th><th>Title</th></tr>";
var x = xmlDoc.getElementsByTagName("CD");
for (i = 0; i <x.length; i++) {
table += "<tr><td>" +
x[i].getElementsByTagName("ARTIST")[0].childNodes[0].nodeValue +
"</td><td>" +
x[i].getElementsByTagName("TITLE")[0].childNodes[0].nodeValue +
"</td></tr>";
}
document.getElementById("demo").innerHTML = table;
}
</script>

</body>
</html>

Fetch API

fetch()XMLHttpRequest 的升级版,用于在 JavaScript 脚本里面发出 HTTP 请求。
浏览器原生提供这个对象。

基本用法

fetch()的功能与 XMLHttpRequest 基本相同,但有三个主要的差异:

1、fetch()使用 Promise,不使用回调函数,因此大大简化了写法,写起来更简洁。 2、fetch()采用模块化设计,API 分散在多个对象上(Response 对象Request 对象Headers 对象),更合理一些;相比之下,XMLHttpRequest 的 API 设计并不是很好,输入、输出、状态都在同一个接口管理,容易写出非常混乱的代码。 3、fetch()通过数据流(Stream 对象)处理数据,可以分块读取,有利于提高网站性能表现,减少内存占用,对于请求大文件或者网速慢的场景相当有用。XMLHttpRequest 对象不支持数据流,所有的数据必须放在缓存里,不支持分块读取,必须等待全部拿到后,再一次性吐出来。

在用法上,fetch()接受一个 URL 字符串作为参数,默认向该网址发出 GET 请求,返回一个 Promise 对象。

fetch(url)
.then(...)
.catch(...)

例子,从服务器获取json:

fetch('https://api.github.com/users/can-dy-jack')
.then(response => response.json())
.then(json => console.log(json))
.catch(error => console.log('Request Failed', error));

fetch()接收到的response是一个 Stream 对象response.json()是一个异步操作,取出所有内容,并将其转为 JSON 对象。

注:Promise 对象用于表示一个异步操作的最终完成 (或失败)及其结果值。

Promise 可以使用 await 语法改写,使得语义更清晰。

async function getJSON() {
let url = 'https://api.github.com/users/ruanyf';
try {
let response = await fetch(url);
return await response.json();
} catch (error) {
console.log('Request Failed', error);
}
}
console.log(getJSON()); // 调用getJSON(),并输出获取到的Promise

await语句必须放在try...catch里面,这样才能捕捉异步操作中可能发生的错误。

Response 对象

Response 对象:处理 HTTP 回应。

Response 对象的同步属性

fetch()请求成功以后,得到的是一个 Response 对象。它对应服务器的 HTTP 回应。

Response包含的数据通过 Stream 接口异步读取,但是它还包含一些同步属性,对应 HTTP 回应的标头信息(Headers),可以立即读取。
示例:

async function fetchText() {
let response = await fetch('/readme.txt');
console.log(response.status);
console.log(response.statusText);
}

response.statusresponse.statusText就是 Response 的同步属性,可以立即读取。

标头信息属性有下面这些(more: MDN-API/Response | Response 对象):

  • Response.ok
  • Response.statusText
  • Response.status
  • Response.url
  • Response.type
  • ... ...

判断请求是否成功

fetch()发出请求以后,只有网络错误,或者无法连接时,fetch()才会报错,其他情况都不会报错,而是认为请求成功。

这就是说,即使服务器返回的状态码是 4xx5xx,fetch()也不会报错(即 Promise 不会变为 rejected状态)。

只有通过Response.status属性,得到 HTTP 回应的真实状态码,才能判断请求是否成功。

async function fetchText() {
let response = await fetch('https://api.github.com/users/can-dy-jack');
if (response.status >= 200 && response.status < 300) {
return await response.text();
} else {
throw new Error(response.statusText);
}
}

另一种方法是判断Response.oK是否为true。

Response.headers 属性

Response 对象还有一个Response.headers属性,指向一个 Headers 对象,对应 HTTP 回应的所有标头。

Headers 对象可以使用for...of循环进行遍历。

async function test(){
const response = await fetch('https://api.github.com/users/ruanyf');
for (let [key, value] of response.headers) {
console.log(`${key} : ${value}`);
}
// 或者
/*
for (let [key, value] of response.headers.entries()) {
console.log(`${key} : ${value}`);
}
*/
}
test();

Headers 对象提供了以下方法,用来操作标头。

读取内容的方法

Response对象根据服务器返回的不同类型的数据,提供了不同的读取方法。

  • response.text():得到文本字符串。
  • response.json():得到 JSON 对象。
  • response.blob():得到二进制 Blob 对象。
  • response.formData():得到 FormData 表单对象。
  • response.arrayBuffer():得到二进制 ArrayBuffer 对象。

上面5个读取方法都是异步的,返回的都是 Promise 对象。必须等到异步操作结束,才能得到服务器返回的完整数据。

Stream 对象只能读取一次,读取完就没了。这意味着,前一节的五个读取方法,只能使用一个,否则会报错。

Response 对象提供Response.clone()方法,创建Response对象的副本,实现多次读取。

Response.body 属性

Response.body属性是 Response 对象暴露出的底层接口,返回一个 ReadableStream 对象,供用户操作。

它可以用来分块读取内容,应用之一就是[显示下载的进度]

fetch()的第二个参数:定制 HTTP 请求

fetch()的第一个参数是 URL,还可以接受第二个参数,作为配置对象,定制发出的 HTTP 请求。

fetch(url, optionObj)

HTTP 请求的方法、标头、数据体都在这个对象里面设置。

Post请求

const response = await fetch(url, {
method: 'POST',
headers: {
"Content-type": "application/x-www-form-urlencoded; charset=UTF-8",
},
body: 'foo=bar&lorem=ipsum',
});

const json = await response.json();

提交json数据

const user =  { name:  'John', surname:  'Smith'  };
const response = await fetch('/article/fetch/post/user', {
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=utf-8'
},
body: JSON.stringify(user)
});

提交表单

const form = document.querySelector('form');

const response = await fetch('/users', {
method: 'POST',
body: new FormData(form)
})

文件上传

const input = document.querySelector('input[type="file"]');

const data = new FormData();
data.append('file', input.files[0]);
data.append('user', 'foo');

fetch('/avatars', {
method: 'POST',
body: data
});

直接上传二进制文件

let blob = await new Promise(resolve =>   
canvasElem.toBlob(resolve, 'image/png')
);

let response = await fetch('/article/fetch/post/image', {
method: 'POST',
body: blob
});

fetch()第二个参数配置对象的完整 API

const response = fetch(url, {
method: "GET",
headers: {
"Content-Type": "text/plain;charset=UTF-8"
},
body: undefined,
referrer: "about:client",
referrerPolicy: "no-referrer-when-downgrade",
mode: "cors",
credentials: "same-origin",
cache: "default",
redirect: "follow",
integrity: "",
keepalive: false,
signal: undefined
});