Ajax
Ajax 是异步的 JavaScript 和 XML。简单来说就是使用
XMLHttpRequest
对象和服务器通信。可以使用 JSON、XML、HTML 和 text 文本格式来发送和接收数据。具有异步的特性,可在不刷新页面的情况下实现和服务器的通信,交换数据或者更新页面
01. 体验 Ajax
使用 axios 库,与服务器之间进行数据通信,这个库就是基于上面提到的 XMLHttpRequest
的封装,代码简洁,在后面的 Vue、React 项目中都会使用到这个库。
-
引入
axios.jx
:https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js -
使用
axios
函数来实现请求和接收数据axios({ url: '请求的资源地址' }).then((result)=> { 对数据的处理; })
案例:使用 Ajax 来发送请求获取省份信息并且将其展示在页面上:
<body>
<!--
axios库地址: https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js
省份数据地址: http://ajax-api.itheima.net/api/province
-->
<p></p>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
const p = document.querySelector('p');
axios({
url: "http://ajax-api.itheima.net/api/province"
}).then(result => {
console.log(result);
p.innerHTML = result.data.data.join('<br>');
})
</script>
</body>
可以多使用 console.log()
语句来检查返回的数据类型
02. 认识 URL
URL:统一资源定位符,速成网页地址,简称网址,是因特网上标准的资源的地址。
URL 的组成
2.1 协议
在计算机科学和网络通信领域,协议(Protocol)是一组规则和约定,用于在计算机系统之间进行通信和数据交换。它定义了通信双方的行为规范,以便在网络中传输数据、执行操作或完成特定任务;协议通常描述了通信的格式、序列、错误检测、数据压缩、加密和身份验证等方面的规则。它们为通信设备和程序之间的互操作性提供了基础,并确保数据的可靠传输和正确处理。
http
协议:超文本传输协议,规定浏览器和服务器之间传输数据的格式
2.2 域名
域名(Domain Name)是互联网中用于识别和定位计算机或网络服务的人类可读的名称。它是互联网上的网站、服务器或其他资源的地址标识;域名通常由多个部分组成,以点号
.
分隔。例如,在域名example.com
中,example
是二级域名,.com
是顶级域名(TLD)。域名可以有不同层次的子域,例如www.example.com
中的www
是三级子域。
一些常见的顶级域名包括:
- 通用顶级域名(gTLD):如
.com
(商业)、.org
(非营利组织)、.net
(网络服务)等。 - 国家和地区代码顶级域名(ccTLD):如
.cn
(中国)、.uk
(英国)、.jp
(日本)等,代表特定国家或地区。
标记服务器在浏览器中的方位
2.3 资源路径
标记资源在服务器中的具体位置,根据这个请求去访问后端不同的组件或者资源
2.4 URL 查询参数
浏览器提供给服务器的额外信息,让服务器返回浏览器想要的数据
语法:http://xxx.com/xxx/xxx?参数名1=值1&参数名2=值2
但真实这样写的话容易出现错误,这里来看 axios
提供给我们的添加查询参数的方式:
axios({
url: '请求地址',
// 查询参数
params: {
参数名: '参数'
}
}).then(result => {
数据处理;
})
案例:向请求地址中发送带参数的请求实现查询省份的市
<body>
<p></p>
<!--
城市列表: http://hmajax.itheima.net/api/city
参数名: pname
值: 省份名字
-->
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
const p = document.querySelector('p');
axios({
url: 'http://hmajax.itheima.net/api/city',
// 查询参数
params: {
pname: '河北省'
}
}).then(result => {
console.log(result.data.list.join('<br>'));
p.innerHTML = result.data.list.join('<br>');
})
</script>
</body>
比如上面的方式实际上就是给服务器发送了一个 GET
请求,并且携带了参数,参数名为 pname
参数值为我们查询的省份,上面是 河北省
,当后端接收到这个请求后就会返回我们需要的数据
2.4 案例
根据省份和城市的名字来查询地址列表,省份和城市需要用户自己输入
显示效果:
<body>
<div class="container">
<form id="editForm" class="row">
<!-- 输入省份名字 -->
<div class="mb-3 col">
<label class="form-label">省份名字</label>
<input type="text" value="北京" name="province" class="form-control province" placeholder="请输入省份名称" />
</div>
<!-- 输入城市名字 -->
<div class="mb-3 col">
<label class="form-label">城市名字</label>
<input type="text" value="北京市" name="city" class="form-control city" placeholder="请输入城市名称" />
</div>
</form>
<button type="button" class="btn btn-primary sel-btn">查询</button>
<br><br>
<p>地区列表: </p>
<ul class="list-group">
<!-- 示例地区 -->
<li class="list-group-item">东城区</li>
</ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
/*
获取地区列表: http://hmajax.itheima.net/api/area
查询参数:
pname: 省份或直辖市名字
cname: 城市名字
*/
const btn = document.querySelector('.sel-btn')
btn.addEventListener('click', function() {
const province = document.querySelector('.province').value;
const city = document.querySelector('.city').value;
const ur = document.querySelector('.list-group');
axios({
url: 'http://hmajax.itheima.net/api/area',
params: {
pname: province,
cname: city
}
}).then(result => {
const NewArr = result.data.list.map(function(ele) {
return `<li class="list-group-item">${ele}</li>`;
})
ur.innerHTML = NewArr;
})
})
</script>
</body>
请求除北京以外的其他地址可能有问题,保证自己能够学会如何发送多参数的请求即可
03. 常用的请求方法
对服务器的资源,要执行的操作
请求方法 | 操作 |
---|---|
GET | 获取数据 |
POST | 提交数据 |
PUT | 修改数据(全部) |
DELETE | 删除数据 |
PATCH | 修改数据(部分) |
可以理解为给后端传递的某种标识,后端开发人员在得到这个标识后就知道了你想做什么操作,由此来书写逻辑
3.1 axios 请求配置
url
请求的 URL 地址method
请求的方法,GET 为默认请求方法data
提交的数据
案例:实现注册功能,向服务器提供账号和密码,在控制台打印出返回的信息
<body>
<button class="btn">注册用户</button>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
/*
注册用户: http://hmajax.itheima.net/api/register
请求方法: POST
参数名:
username: 用户名 (中英文和数字组成, 最少8位)
password: 密码 (最少6位)
目标: 点击按钮, 通过axios提交用户和密码, 完成注册
*/
document.querySelector('button').addEventListener('click', () => {
axios({
url: 'http://hmajax.itheima.net/api/register',
method: 'post',
params: {
username: 'kq',
password: '123456'
}
}).then(result => {
console.log(result);
})
})
</script>
</body>
04. axios 的错误处理
在网络通信中,错误信息与正常信息通常可以通过 HTTP 响应的状态码(Status Code)来区分。HTTP 状态码是服务器向客户端返回的一个三位数字代码,表示服务器对请求的处理结果。
语法:在 then
方法的后面,调用 catch
方法,传入回调函数并且定义形参
axios({
url: '请求地址',
// 查询参数
params: {
参数名: '参数'
}
}).then(result => {
数据处理;
}).catch(error => {
错误处理;
})
<body>
<button class="btn">注册用户</button>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
/*
注册用户: http://hmajax.itheima.net/api/register
请求方法: POST
参数名:
username: 用户名 (中英文和数字组成, 最少8位)
password: 密码 (最少6位)
目标: 点击按钮, 通过axios提交用户和密码, 完成注册
*/
document.querySelector('.btn').addEventListener('click', () => {
axios({
url: 'http://hmajax.itheima.net/api/register',
// 指定请求方法
method: 'post',
data: {
username: 'itheima007',
password: '7654321'
}
}).then(result => {
console.log(result)
}).catch(error => {
alert(error.response.data.message);
})
})
</script>
</body>
05. HTTP 报文
HTTP 协议规定了浏览器发送以及服务器返回的内容的格式,而请求报文就是浏览器按照 HTTP 协议要求的格式,发送给服务器的内容
5.1 请求报文的格式
请求报文的组成部分有:
- 请求行:带有请求方法、URL 和协议的信息
- 请求头:以键值对的格式携带的信息
- 空行:分割请求头,空行之后是发送给服务器的资源
- 请求体:发送的资源
比如上面案例中发送的请求可以通过控制台中的网络模块清晰的看到
请求行和请求头:
请求体:
分别是在标头部分和载荷部分通过查看源代码的方式显示出来的
5.2 错误排查
可以通过查看请求报文中的信息来排查错误,看看是否是因为代码逻辑导致请求信息有问题
5.3 响应报文
响应报文就是服务器按照 HTTP 协议的要求的格式,返回给浏览器的内容
响应报文的组成部分有:
- 请求行(状态行):协议、HTTP 响应状态码、状态信息
- 响应头:以键值对的格式携带的附加信息
- 空行:分割响应头,空行之后是服务器返回的资源
- 响应体:返回的资源
响应报文中另外一个需要了解的是响应状态码
状态码 | 说明 |
---|---|
1xx | 信息 |
2xx | 成功 |
3xx | 重定向消息 |
4xx | 客户端出现的错误(比如浏览器) |
5xx | 服务端出现的错误(比如后端代码中的逻辑错误) |
06.接口文档
描述接口的文章,接口就是我们使用 Ajax 与服务器通讯的时候,使用 URL、请求方法以及携带的参数。
比如上面讲到的请求城市列表的案例,它的接口文档就是这样的
前后端的开发都依靠这个接口文档进行,前端开发用于明确请求参数、请求地址和返回的信息然后将它们展示出来,后端开发根据这个接口文档去书写不同的方法,来接收请求、实现业务逻辑和返回信息。
07. form-serialize 插件
这个插件可以帮助我们快速的收集表单元素的值;
在前面写获取表单数据的时候,要一个个去获取表单中的元素和获取它们的值,通过这个插件可以帮助我们简化这个步骤
<body>
<form action="javascript:;" class="example-form">
<input type="text" name="uname">
<br>
<input type="text" name="pwd">
<br>
<input type="button" class="btn" value="提交">
</form>
<!--
目标:在点击提交时,使用form-serialize插件,快速收集表单元素值
-->
<!--引入 js 文件-->
<script src="./lib/form-serialize.js"></script>
<script>
document.querySelector('.btn').addEventListener('click', () => {
const data = serialize(document.querySelector('.example-form'),
{ hash: true,empty: true});
console.log(data);
})
</script>
</body>
- 引入
form-seralize
文件:我们实际上是去调用这个插件暴露给我们的seralize
方法 - 调用方法传递参数,参数有
- 我们需要处理的表单对象
- 配置对象:
hash
设置获取的数据结构(获取的是 js 对象还是查询字符串)、empty
设置是否获取空值
- 获取返回结果
- 如果
hash
设置的是true
则获得一个 js 对象 - 如果
hash
设置为false
获取的是查询字符串
- 如果
比如在上面的案例中,获取到的就是这样一个对象,对象的属性名为表单项设置的 name
属性
实际开发中 name
属性根据接口文档来设置,hash
和 empty
均设置为 true
07. 图片上传
- 获取图片文件对象:当我们上传图片之后,浏览器会创建一个
File
对象来存储我们上传的图片的所有数据,我们需要获取到这个对象 - 使用 FormData 携带图片文件:这个方法可以实现表单的提交方式,即采用特定的结构
- 提交表单数据给服务器
<body>
<!-- 文件选择元素 -->
<input type="file" class="upload">
<img src="" alt="" class="my-img">
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
/**
* 目标:图片上传,显示到网页上
* 1. 获取图片文件
* 2. 使用 FormData 携带图片文件
* 3. 提交到服务器,获取图片url网址使用
*/
document.querySelector('.upload').addEventListener('change', e => {
const fd = new FormData();
// 获取图片文件对象:e.target.files[0]
// 使用 FormData 携带图片文件:fd.append('img', e.target.files[0]);
fd.append('img', e.target.files[0]);
console.log(e.target.files[0]);
// 提交表单数据给服务器
axios({
url: 'http://hmajax.itheima.net/api/uploadimg',
method: 'post',
data: fd
}).then(result => {
console.log(result);
document.querySelector('img').src = result.data.data.url;
})
})
</script>
</body>
08. XMLHttpRequest
XMLHttpRequest
对象用于与服务器的交互。通过XMLHttpRquest
可以在不刷新页面的情况下请求特定的 URL,获取数据。这允许网页在不影响用户操作的情况下,更新页面的局部内容。这个对象在AJAX
编程中被大量的使用。axios
的内部就是采用XMLHttpRequest
与服务器交互。
8.1 发送基本请求
使用步骤:
- 创建
XMLHttpRequest
对象 - 配置请求方法和请求 URL 地址
- 监听 loadend 时间,接收响应结果
- 发送请求给服务器
<body>
<p></p>
<script>
/**
* 目标:使用XMLHttpRequest对象与服务器通信
* 1. 创建 XMLHttpRequest 对象
* 2. 配置请求方法和请求 url 地址
* 3. 监听 loadend 事件,接收响应结果
* 4. 发起请求
*/
const xhr = new XMLHttpRequest();
const p = document.querySelector('p');
xhr.open('GET', 'https://hmajax.itheima.net/api/province');
xhr.addEventListener('loadend', () => {
const data = JSON.parse(xhr.response);
p.innerHTML = data.list.join('<br>');
})
xhr.send();
</script>
</body>
再来看看 axios
的代码
<body>
<!--
axios库地址: https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js
省份数据地址: http://ajax-api.itheima.net/api/province
-->
<p></p>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
const p = document.querySelector('p');
axios({
url: "http://ajax-api.itheima.net/api/province"
}).then(result => {
console.log(result);
p.innerHTML = result.data.data.join('<br>');
})
</script>
</body>
axios
帮助我们发送请求并且将返回的结果封装到 result
对象中,数据挂载到 data
属性上,不需要我们进行过多的操作
8.2 复杂请求的发送
带参数的 GET 请求:
- 在
axios
中是使用配置对象的data
参数来给实现参数的传递,原生方式下就只能自己将请求放置到请求地址的后面http://hmajax.itheima.net/api/area?参数名1=值1&参数名2=值
发送带有 JSON 对象的请求:
- 核心:请求头中设置
Content-Type: appliocation/json
- 请求体中携带
JSON
字符串
<body>
<script>
xhr.open('请求方法', '请i取地址');
xhr.addEventListener('loadend', () => {
console.log(xhr.response);
})
// 告诉服务器传递的参数类型是 JSON 字符串
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON 字符串);
</script>
</body>
09. Promise
Promise
对象用于标识一个异步操作的最终完成(或者失败)及其结果值,axios
的底层也是使用了这个对象
9.1 模拟 axios 请求
<body>
<script>
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('请求成功');
// reject(new Error('请求失败'));
}, 2000)
})
p.then(result => {
console.log(result);
}).catch(error => {
console.log(error);
})
</script>
</body>
- 创建一个新的
Promise
对象,向里面传入一个执行器函数作为参数,JavaScript 引擎会自动将resolve
和reject
两个函数传递进来,当执行成功后调用resolve
函数,执行失败后调用reject
函数 - 这时候再调用
Promise
对象的then
或者catch
方法来获取我们调用上面两个函数时传入的数据
9.2 Promise 对象的三种状态
一个 Promise
对象必然处于以下几种状态之一
- 待定状态:初始状态,既没有被兑现也没有被拒绝
- 已兑现状态:调用了
resolve
函数,表明操作已经完成,会调用then()
中传入的回调函数 - 已拒绝状态:意味着操作失败,调用了
reject
函数,会调用catch()
中传入的回调函数
当这个对象达到已兑现状态或已拒绝状态的时候,状态就已经敲定了,不能再改变
10. 同步代码和异步代码
同步代码:浏览器是按照我们书写代码的顺序一行一行的执行程序的,浏览器会等待代码的解析和工作,在上一个语句执行完成后才会去执行下一条语句,这样做是很有必要的,因为每一行新的代码都是建立在前面代码的基础上的。
异步代码:异步编程技术使得程序在执行一个可能长期运行的任务的时候对其他的事件作出反应,从而不必等待这个时间很长的任务的完成,是在调用后耗时,不阻塞代码的继续执行,在将来完成后回来触发一个回调函数。
10.1 async 函数和 await
async
函数是使用async
关键字声明的函数。这个函数是AsyncFunction
构造函数的实例,并且其中允许使用await
关键字。它们的作用是用一种更加高效简洁的方式写出基于Promise
异步执行的代码,无需再去考虑链式调用的问题。
在 async 函数内,使用 await
关键字取代 then 函数,等待获取 Promise 对象成功状态的结果值
/**
* 目标:掌握async和await语法,解决回调函数地狱
* 概念:在async函数内,使用await关键字,获取Promise对象"成功状态"结果值
* 注意:await必须用在async修饰的函数内(await会阻止"异步函数内"代码继续执行,原地等待结果)
*/
async function getData() {
const pRes = await axios({
url: 'http://hmajax.itheima.net/api/province'
});
pname = pRes.data.list[0];
document.querySelector('.province').innerHTML = pname;
const cityRes = await axios({
url: 'http://hmajax.itheima.net/api/city',
params: {
pname: pname
}
});
cname = cityRes.data.list[0];
document.querySelector('.city').innerHTML = cname;
const aRes = await axios({
url: 'http://hmajax.itheima.net/api/area',
params: {
pname: pname,
cname: cname
}
});
aName = aRes.data.list[0];
document.querySelector('.area').innerHTML = aName;
}
getData();
会等待我们上面的 await
修饰的函数完成后再去进行别的操作
10.2 async 函数和 await 捕获错误
async function getData() {
try {
const pRes = await axios({
url: 'http://hmajax.itheima.net/api/province'
});
pname = pRes.data.list[0];
document.querySelector('.province').innerHTML = pname;
const cityRes = await axios({
url: 'http://hmajax.itheima.net/api/city',
params: {
pname: pname
}
});
cname = cityRes.data.list[0];
document.querySelector('.city').innerHTML = cname;
const aRes = await axios({
url: 'http://hmajax.itheima.net/api/area',
params: {
pname: pname,
cname: cname
}
});
aName = aRes.data.list[0];
document.querySelector('.area').innerHTML = aName;
}catch (error) {
console.log(error);
}
}
10.3 事件循环
JavaScript 有一个基于事件循环的并发模型,事件循环负责执行代码、收集和处理时间以及执行后续队列中的子任务。这个模型与其他语言中的模型截然不同。这是因为 JavaScript 是单线程 ,为了不让耗时代码阻塞其他代码的执行,设计了事件循环模型。