AJAX
一、AJAX基础
1、 AJAX 有什么用
浏览器和服务器之间通信,动态数据交互
2、axios库的使用
引入axios库
html
<script src="https://cdn.bootcdn.net/ajax/libs/axios/1.3.6/axios.js"></script>
使用axios函数
js
axios({
url: 'http://hmajax.itheima.net/api/province'
}).then(result => {
document.write(result['data']['list'].join('<br>'))
})
3、URL查询参数
浏览器提供给服务器的额外信息,让服务器返回浏览器想要的数据
js
axios({
url: 'http://hmajax.itheima.net/api/city',
params: {
pname: '河南省'
}
}).then(result => {
document.write(result.data.list.join(' '))
})
4、请求方法与错误处理
js
axios({
url: 'http://hmajax.itheima.net/api/register',
method: 'post',
// 请求体携带的信息
data: {
username,
password
}
}).then(result => {
p.innerText = result['data']['message']
}).catch(error => {
p.innerText = error['response']['data']['message']
})
5、form-serialize插件
作用:快速收集表单元素的值
js
document.querySelector('button').addEventListener('click', () => {
const form = document.querySelector('.test-form')
const data = serialize(form, {
hash: true,
empty: true
})
console.log(data)
})
- hash:设置获取数据结构(json对象、查询字符串)
- empty:设置是否获取空值(获取、不获取)
6、图片上传
- 先获取图片文件对象
- 使用 FormData 表单数据对象装入(因为图片是文件而不是以前的数字和字符串了所以传递文件一般需要放入 FormData 以键值对-文件流的数据传递)
js
const fd = new FormData()
fd.append(参数名, 值)
js
document.querySelector('.upload').addEventListener('change', e => {
// 1. 获取图片文件
console.log(e.target.files[0])
// 2. 使用 FormData 携带图片文件
const fd = new FormData()
fd.append('img', e.target.files[0])
// 3. 提交到服务器,获取图片url网址使用
axios({
url: 'http://hmajax.itheima.net/api/uploadimg',
method: 'POST',
data: fd
}).then(result => {
console.log(result)
// 取出图片url网址,用img标签加载显示
const imgUrl = result.data.data.url
document.querySelector('.my-img').src = imgUrl
})
})
7、补充
1)本地存储
js
// 设置本地存储
localStorage.setItem('url', imgUrl)
// 读取本地存储
const imgUrl = localStorage.getItem('url')
2)设置基地址
js
axios.default.baseURI = 'http://geek.itheima.net'
二、AJAX原理
1、XMLHttpRequest
1)查询GET
js
const xhr = new XMLHttpRequest()
const pname = '河北省'
xhr.open('get', `http://hmajax.itheima.net/api/city?pname=${pname}`)
xhr.addEventListener('loadend', e => {
console.log(JSON.parse(e.explicitOriginalTarget.responseText).list)
})
xhr.send()
使用urlSearchParams
对象简化操作
js
const obj = {
name: 'wmh',
gender: 'man'
}
console.log((new URLSearchParams(obj)).toString())
// 'name=wmh&gender=man'
2)提交POST
- 请求头:设置Content-Type: application/json
- 请求体:携带JSON字符串
js
const xhr = new XMLHttpRequest()
xhr.open('POST','http://hmajax.itheima.net/api/register')
// 设置Content-Type: application/json
xhr.setRequestHeader('Content-Type', 'application/json')
xhr.addEventListener('loadend', e => {
console.log(e.explicitOriginalTarget.response)
})
const obj = {
username : 'wmh12345678',
password : 'wmh123456',
}
// 携带JSON字符串
xhr.send(JSON.stringify(obj))
2、Promise
1)使用
Promise:一个异步操作最终状态和结果值的对象
用于管理异步操作的代码,解决回调函数地狱的问题
js
// 1. 创建 Promise 对象
const p = new Promise((resolve, reject) => {
// 2. 执行异步任务-并传递结果
// 成功调用: resolve(值) 触发 then() 执行
// 失败调用: reject(值) 触发 catch() 执行
})
// 3. 接收结果
p.then(result => {
// 成功
}).catch(error => {
// 失败
})
通过status
状态码判断是否请求成功
js
const p = new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.open('POST', 'http://hmajax.itheima.net/api/register')
xhr.setRequestHeader('Content-Type', 'application/json')
xhr.addEventListener('loadend', e => {
if (e.explicitOriginalTarget.status >= 200 && e.explicitOriginalTarget.status < 300) {
resolve('注册成功')
} else {
reject(new Error('注册失败'))
}
})
const obj = {
username: 'wmh123456',
password: 'wmh123456',
}
xhr.send(JSON.stringify(obj))
})
p.then(r => {
console.log(r)
}).catch(e => {
console.log(e)
})
2)三种状态
- 待定(pending):初始状态,既没有被兑现,也没有被拒绝
- 已兑现(fulfilled):操作成功完成
- 已拒绝(rejected):操作失败
每个 Promise 对象一旦被兑现/拒绝,那就是已敲定了,状态无法再被改变
三、AJAX进阶
1、同步与异步
- 同步代码:逐行执行,需原地等待结果后,才继续向下执行
- 异步代码:调用后耗时,不阻塞代码继续执行(不必原地等待),在将来完成后触发回调函数传递结果
2、回调函数地狱
在回调函数中嵌套回调函数,一直嵌套下去就形成了回调函数地狱
缺点:可读性差,异常无法捕获,耦合性严重,牵一发动全身
js
let pname, cname
axios({
url: 'http://hmajax.itheima.net/api/province'
}).then(r => {
pname = r.data.list[0]
document.querySelector('.province').innerHTML = pname
axios({
url: 'http://hmajax.itheima.net/api/city',
params: {pname}
}).then(r => {
cname = r.data.list[0]
document.querySelector('.city').innerHTML = cname
axios({
url: 'http://hmajax.itheima.net/api/area',
params: {pname, cname}
}).then(r => {
document.querySelector('.area').innerHTML = r.data.list[0]
})
})
})
3、Promise 链式调用
Promise 链式调用:依靠 then() 方法会返回一个新生成的 Promise 对象特性,继续串联下一环任务,直到结束
- 细节:then() 回调函数中的返回值,会影响新生成的 Promise 对象最终状态和结果
- 好处:通过链式调用,解决回调函数嵌套问题
js
let pname = ''
axios({url: 'http://hmajax.itheima.net/api/province'}).then(result => {
pname = result.data.list[0]
document.querySelector('.province').innerHTML = pname
return axios({url: 'http://hmajax.itheima.net/api/city', params: {pname}})
}).then(result => {
const cname = result.data.list[0]
document.querySelector('.city').innerHTML = cname
return axios({url: 'http://hmajax.itheima.net/api/area', params: {pname, cname}})
}).then(result => {
console.log(result)
const areaName = result.data.list[0]
document.querySelector('.area').innerHTML = areaName
})
4、async、await函数
在 async 函数内,使用 await 关键字取代 then 函数,等待获取 Promise 对象成功状态的结果值
js
async function getData() {
const province = await axios({url: 'http://hmajax.itheima.net/api/province'})
const pname = province.data.list[0]
const city = await axios({url: 'http://hmajax.itheima.net/api/city', params: {pname}})
const cname = city.data.list[0]
const area = await axios({url: 'http://hmajax.itheima.net/api/area', params: {pname, cname}})
const aname = area.data.list[0]
document.querySelector('.area').innerHTML = aname
document.querySelector('.province').innerHTML = pname
document.querySelector('.city').innerHTML = cname
}
getData()
5、try catch语句
语句标记要尝试的语句块,并指定一个出现异常时抛出的响应
try 里有报错的代码,会立刻跳转到 catch 中
js
try {
// 要执行的代码
} catch (error) {
// error 接收的是,错误消息
// try 里代码,如果有错误,直接进入这里执行
}
6、事件循环
- 作用:事件循环负责执行代码,收集和处理事件以及执行队列中的子任务
- 原因:JavaScript 单线程(某一刻只能执行一行代码),为了让耗时代码不阻塞其他代码运行,设计了事件循环模型
- 概念:执行代码和收集异步任务的模型,在调用栈空闲,反复调用任务队列里回调函数的执行机制,就叫事件循环
7、宏任务与微任务
- 宏任务:由浏览器环境执行的异步代码
- 微任务:由 JS 引擎环境执行的异步代码
宏任务每次在执行同步代码时,产生微任务队列,清空微任务队列任务后,微任务队列空间释放!
下一次宏任务执行时,遇到微任务代码,才会再次申请微任务队列空间放入回调函数消息排队
总结:一个宏任务包含微任务队列,他们之间是包含关系,不是并列关系
8、Promise.all 静态方法
合并多个 Promise 对象并等待所有同时成功的结果,如果有一个报错就会最终为失败状态,当需要同时渲染多个接口数据同时到网页上时使用
js
const p = Promise.all([Promise对象, Promise对象, ...])
p.then(result => {
// result 结果: [Promise对象成功结果, Promise对象成功结果, ...]
}).catch(error => {
// 失败的 Promise 抛出的异常对象
})