概述
背景
在了解 AJAX 之前我们可以简单的认为「JavaScript 能力有限」,因为在此之前 Web 平台 提供所有的 API 都只停留在「单机」的阶段。这样就会造成一些无法实现的功能,例如:
- 无法在实现用户登录功能时,当用户输入邮箱地址显示用户对应的头像
- 无法在实现用户注册功能时,当用户输入邮箱或者用户名就提示是否存在
- 无法在实现留言板功能时,实时看到最新的用户留言
这些功能的开发最终都卡在一个相同的问题上:数据存放在服务端,无法通过已知的 API 获取。已知发送请求的方式:
- 地址栏输入地址,回车,刷新
- 特定元素的 href 或 src 属性
- 表单提交
这些方案都是我们无法通过或者很难通过代码的方式进行编程操作的。当需要对服务端发出请求并且接受服务端返回的响应时,我们可以通过 JavaScript 直接发送网络请求,那么 Web 的可能就会更多,随之能够实现的功能也会更多,至少不再是只能开发「单机游戏」。
Google Suggest
AJAX(Asynchronous JavaScript and XML),最早出现在 2005 年的 Google Suggest 上。它不是像 HTML、JavaScript 或 CSS 这样的一种“正式的”技术,它是在浏览器端进行网络编程(发送请求、接收响应)的技术方案,它使我们可以通过 JavaScript 直接获取服务端最新的内容而不必重新加载页面,让 Web 更能接近桌面应用的用户体验。
AJAX
AJAX 就是浏览器提供的一套 API,可以通过 JavaScript 调用,从而实现通过代码控制请 求与响应。实现通过 JavaScript 进行网络编程。XML 是最早在客户端与服务端之间传递数据时所采用的数据格式。应用于按需获取数据 、对用户数据校验、自动更新页面内容、提升用户无刷新体验等场景中。
免费数据接口:https://jsonplaceholder.typicode.com/
原生 AJAX
发送 AJAX 请求步骤:1、创建 XMLHttpRequest 类型的对象;2、准备发送,打开与一个网址之间的连接;3、执行发送动作;4、指定 xhr 状态变化事件处理函数。
// 以下为拉勾教育课件内代码
// 1.创建一个 XMLHttpRequest 类型的对象 --- 相当于打开了一个浏览器
var xhr = new XMLHttpRequest();
// 2.打开一个与网址之间的连接 --- 相当于在地址栏输入网址
xhr.open("GET","https://jsonplaceholder.typicode.com/users");
// 3.通过连接发送一次请求 --- 相当于点击回车或者超链接
xhr.send(null);
// 4.指定 xhr 状态变化事件处理函数 --- 相当于处理网页呈现后的操作
xhr.onreadystatechange = function () {
// 通过判断 xhr 的 readyState ,确定此次请求是否完成
if (this.readyState === 4) {
console.log(this.responseText)
}
}
XMLHttpRequest 类型对象
AJAX API 中核心提供的是一个 XMLHttpRequest 类型,该类型是 window 的一个对象类型,所有的 AJAX 操作都需要使用到这个类型,但是存在一定的兼容问题,在 IE6 中并没有这个对象,因此需要兼容写法。
// 以下为拉勾教育课件内代码
// 1.创建一个 XMLHttpRequest 类型的对象
var xhr = null;
// 兼容写法
if (window.XMLHttpRequest) {
// 标准浏览器
xhr = new XMLHttpRequest();
} else {
// IE 6 浏览器
xhr = new ActiveXObject("Microsoft.XMLHTTP");
}
open() 方法开启请求
本质上 XMLHttpRequest 就是 JavaScript 在 Web 平台中发送 HTTP 请求的手段,所以我们发送出去的请求仍然是 HTTP 请求,同样符合 HTTP 约定的格式。
语法:xhr.open(method, url)
- method:要使用的HTTP方法,字符串格式,比如「GET」、「POST」、「PUT」、「DELETE」、等。
- url:要向其发送请求的 URL 地址,字符串格式
// open() 方法开启请求
xhr.open("GET","https://jsonplaceholder.typicode.com/users?id=1");
xhr.open("POST","https://jsonplaceholder.typicode.com/users");
setRequestHeader() 方法设置请求头
该方法必须在 open() 方法和 send() 之间调用,一般 get 方法不需要设置,而 post 方法必须设置。
语法:xhr.setRequestHeader(header, value);
- header: 一般设置 “Content-Type” ,传输数据类型,即服务器需要我们传送的数据类型
- value: 具体的数据类型,常用 "application/x-www-form-urlencoded" 和 "application/json"。
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
send() 方法发送请求
该方法用于发送 HTTP 请求。
语法:xhr.send(body)
- body:在XHR请求中要发送的数据体,根据请求头中的类型进行传参。
如果是 GET 方法,无需设置数据体,可以传 null 或者不传参,如果想传参数则直接写在网站上。
xhr.send("name=harry&age=19");
readyState 属性
readyState 属性返回一个 XMLHttpRequest 代理当前所处的状态,由于 readystatechange 事件是 在 xhr 对象状态变化时触发(不单是在得到响应时),也就意味着这个事件会被触发多次。
readyState | 状态描述 | 说明 |
---|---|---|
0 | UNSENT | 代理 XHR 被创建,但尚未调用 open() 方法 |
1 | OPENED | open() 方法已经被调用,建立了连接。 |
2 | HEADERS_RECEIVED | send() 方法已经被调用,并且已经可以获取状态行和响应头 |
3 | LOADING | 响应体下载中, responseText 属性可能已经包含部分数据 |
4 | DONE | 响应体下载完成,可以直接使用 responseText |
事件处理函数
一般都是在 readyState 值为 4 时,执行响应的后续逻辑。
xhr.onreadystatechange = function () {
// 通过判断 xhr 的 readyState ,确定此次请求是否完成
if (this.readyState === 4) {
// 后续逻辑......
}
同步与异步
同步:可以理解为一个人在同一个时刻只能做一件事情,在执行一些耗时的操作(不需要看管)不去 做别的事,只是等待。
异步:可以理解为在执行一些耗时的操作(不需要看管)去做别的事,而不是等待。
在 Ajax 中,xhr.open()
方法第三个参数要求传入的是一个 boolean 值,其作用就是设置此次请求是否采用异步方式执行,默认为 true 异步,如果需要同步执行可以通过传递 false 实现。但如果如果采用同步方式执行,按照前面的代码编写顺序会卡死在 xhr.send()
这一步。
为了让这个事件可以更加可靠(一定触发),在发送请求 send() 之前,一定是先注册 readystatechange 这样不论是同步或异步都能触发成功,但是在工作中,没有特殊需求不要使用同步模式。
响应数据格式
XML
一种数据描述手段,基本现在的项目不用了,淘汰的原因:数据冗余太多。
JSON
JavaScript Object Notation,JavaScript 对象表示法,该方法也是一种数据描述手段,类似于 JavaScript 字面量方式,服务端采用 JSON 格式返回数据,客户端按照 JSON 格式解析数据。
JSON 格式的数据与 js 对象的区别:1.JSON 数据不需要存到变量中;2.结束时不需要写分号;3.JSON 数据中的属性名必须加引号。
// js 对象字面量
var obj = { name: "tom", age: 19 };
// JSON 格式数据
var str = '{"name": "tom","age": 80}';
不管是 JSON 也好,还是 XML,只是在 AJAX 请求过程中用到,并不代表它们与 AJAX 之 间有必然的联系,它们只是数据协议罢了。不管服务端是采用 XML 还是采用 JSON 本质上都是将数据返回给客户端。服务端应该根据响应内容的格式设置一个合理的 Content-Type。
JSON-Server
平时我们也会自己写一些数据,通过 Ajax 获取,所以需要在本地搭建一个临时服务器。json-server 是一个 Node 模块,运行 Express 服务器,你可以指定一个 json 文件作为 api 的数据源。 也就是说,我们可以使用它快速的搭建一个 web 服务器。
网址:https://github.com/typicode/json-server
原生 Ajax 用法
GET 请求
通常在一次 GET 请求过程中,参数传递都是通过 URL 地址中的 ?
参数传递。 一般在 GET 请求中,无需设置请求头,也无需设置响应体,可以传 null 或者干脆不传。
var xhr = new XMLHttpRequest();
// 发送 GET 请求
xhr.open("GET", "http://localhost:3000/users?age=19");
xhr.send(null);
POST 请求
POST 请求过程中,都是采用请求体承载需要提交的数据。 并且需要设置请求头中的 Content-Type,以便于服务端接收数据,同时提交到服务端的数据可以通过 send 方法的参数传递。
// 以下为拉勾教育课件内代码
var xhr = new XMLHttpRequest();
// 发送 post 请求
xhr.open("POST","http://localhost:3000/users");
// 设置请求头(两种方式)
// 第一种
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
xhr.send("name=lily&age=19&class=2");
// 第二种
xhr.setRequestHeader("Content-Type","application/json")
// JSON 格式数据可以用字符串格式或者 stringify 的格式进行发送
xhr.send(`{
"name": "lulu",
"age": 18,
"class": 2
}`);
xhr.send(JSON.stringify({
"name": "harry",
"age": 18,
"class": 1
}));
处理响应数据渲染
客户端中拿到请求的数据过后最常见的就是把这些数据呈现到界面上。如果数据结构简单,可以直接通过字符串操作(拼接)的方式处理,但是如果数据过于复杂,字符串拼接维护成本太大,就不推荐了,可以使用模版引擎或者 ES6 提供的模板字符串。
封装 AJAX 库
自己封装一个 AJAX 函数主要是为了了解封装的过程,一般情况在开发中都是使用第三方提供的 AJAX 库,因 为它们可能更加严谨。 为了在后续的开发过程中可以更方便的使用这套 API,一般的做法都是将其封装到一个函数中以便调用。
// 以下为拉勾教育课件内代码
// 封装自己的 Ajax 函数
/**
* 参数1:{string} method 请求方法
* 参数2:{string} url 请求地址
* 参数3:{Object} params 请求参数
* 参数4:{function} done 请求完成后执行的回调函数
*/
function ajax(method, url, params, done) {
// 统一将方法中的字母转大写,便于后面判断
method = method.toUpperCase();
// 书写 IE 6 的兼容
var xhr = window.XMLHttpRequest
? new XMLHttpRequest()
: new ActiveXObject("Microsoft.XMLHTTP");
// 将对象格式的参数转为 urlencoded的格式
var pairs = [];
for (var k in params) {
pairs.push(k + "=" + params[k]);
}
var str = pairs.join("&");
// 判断是否是 get 方法,需要更改 url 的值
if (method === "GET") {
url += "?" + str;
}
// 创建打开一个连接
xhr.open(method, url);
var data = null;
// 如果是 post 方法,需要设置请求头,还有请求体
if (method === "POST") {
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
data = str;
}
xhr.send(data);
// 执行回调函数
xhr.onreadystatechange = function () {
if (this.readyState !== 4) return;
// 执行外部传进来的回调函数即可
// 需要用到响应体
done(JSON.parse(this.responseText));
}
}