Ajax笔记(下)
一、图片上传
1.什么是图片上传?
就是把本地的图片上传到网页显示
2.图片上传怎么做?
先依靠文件选择元素获取用户选择的本地文件,接着提交到服务器保存,服务器会返回图片的url网址,然后把网址加载到img标签的src属性即可显示
3.为什么不直接显示到浏览器上,要放到服务器上呢?
因为浏览器保存时临时的,如果你想随时随地访问图片,需要上传到服务器上
4.图片上传怎么做呢?
(1)先获取图片文件对象
(2)使用FormData表单数据对象装入(因为图片是文件而不是以前的数字和字符了,所以传递文件一般需要放入FormData一键只对文件流的数据传递)
(3)提交表单数据对象,使用服务器返回图片url网址
5.核心代码
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>图片上传</title>
</head><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网址使用*/// 文件选择元素->change改变事件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.urldocument.querySelector('.my-img').src = imgUrl})})</script>
</body></html>
总结(图片上传的思路):
先用文件选择元素,获取到文件对象,然后装入 FormData 表单对象中,再发给服务器,得到图片在服务器的 URL 网址,再通过 img 标签加载图片显示
二、案例-网站-更换背景图
1.步骤:
(1)先获取到用户选择的背景图片,上传并把服务器返回的图片url网址设置给body背景
(2)上传成功时,保存图片url网址到localStorage中
(3)网页运行后,获取localStorage中的图片的url网址使用(并判断本地有图片url网址字符串才设置)
2.核心代码
/*** 目标:网站-更换背景* 1. 选择图片上传,设置body背景* 2. 上传成功时,"保存"图片url网址* 3. 网页运行后,"获取"url网址使用* */
document.querySelector('.bg-ipt').addEventListener('change', e => {// 1. 选择图片上传,设置body背景console.log(e.target.files[0])const fd = new FormData()fd.append('img', e.target.files[0])axios({url: 'http://hmajax.itheima.net/api/uploadimg',method: 'POST',data: fd}).then(result => {const imgUrl = result.data.data.urldocument.body.style.backgroundImage = `url(${imgUrl})`// 2. 上传成功时,"保存"图片url网址localStorage.setItem('bgImg', imgUrl)})
})// 3. 网页运行后,"获取"url网址使用
const bgUrl = localStorage.getItem('bgImg')
console.log(bgUrl)
bgUrl && (document.body.style.backgroundImage = `url(${bgUrl})`)
总结(localStorage取值和复制的语法分别是什么):
localStorage.getItem(‘key’)是取值,localStorage.setItem('key',value)赋值
三、案例-个人信息设置
1.分析:
(1)先完成信息回显
(2)再做头像修改-立刻就更新给此用户
(3)收集个人信息表单-提交保存
(4)提交后反馈结果给用户(提示框)
2.信息渲染
(1)获取数据
(2)渲染到页面上
3.头像修改
步骤:
(1)获取到用户选择的头像文件
(2)调用头像修改接口,并除了头像文件外,还要在FormData表单数据中携带外号
(3)提交到服务器保存此用户对应头像文件,并把返回的头像图片的url网址设置到页面上
(3)注意:重新刷新重新获取,已经是修改后的头像
(4)核心代码:
/*** 目标2:修改头像* 2.1 获取头像文件* 2.2 提交服务器并更新头像* */
// 文件选择元素->change事件
document.querySelector('.upload').addEventListener('change', e => {// 2.1 获取头像文件console.log(e.target.files[0])const fd = new FormData()fd.append('avatar', e.target.files[0])fd.append('creator', creator)// 2.2 提交服务器并更新头像axios({url: 'http://hmajax.itheima.net/api/avatar',method: 'PUT',data: fd}).then(result => {const imgUrl = result.data.data.avatar// 把新的头像回显到页面上document.querySelector('.prew').src = imgUrl})
})
总结:为什么这次上传头像,需要携带外号呢?
因为这次头像上传到后端,是要保存在某个用户名下的,所以需要把外号名字一起携带过去、
4.信息修改:
需求:点击提交按钮,收集个人信息,提交到服务器保存(无需重新获取刷新,因为页面已经是最新的数据了)
(1)收集表单数据
(2)提交到服务器保存-调用用户信息更新接口(注意请求方式是PUT)代表数据更新的意思
核心代码:
/*** 目标3:提交表单* 3.1 收集表单信息* 3.2 提交到服务器保存*/
// 保存修改->点击
document.querySelector('.submit').addEventListener('click', () => {// 3.1 收集表单信息const userForm = document.querySelector('.user-form')const userObj = serialize(userForm, { hash: true, empty: true })userObj.creator = creator// 性别数字字符串,转成数字类型userObj.gender = +userObj.genderconsole.log(userObj)// 3.2 提交到服务器保存axios({url: 'http://hmajax.itheima.net/api/settings',method: 'PUT',data: userObj}).then(result => {})
})
总结:信息修改数据和以前的增删改查哪个实现的思路比较接近呢?
编辑:首先是回显已经做完了,然后收集用户最新改动后的数据,提交到服务器上保存,因为页面最终就是用户刚写的数据,所以不用重新获取并刷新页面了。
5.提示框
(1)需求:使用bootStrap提示框和modal弹框使用很像,语法如下:
首先准备对应的标签结构,然后设置延迟自动显示的时间
<div class="toast" data-bs-delay="1500">
提示框内容
</div>
最后:使用js的方式,在axios请求响应成功时,展示结果
// 创建提示框对象
const toastDom = document.querySelector('css选择器')
const toast = new bootstrap.Toast(toastDom)// 显示提示框
toast.show()
核心代码:
/*** 目标3:提交表单* 3.1 收集表单信息* 3.2 提交到服务器保存*/
/*** 目标4:结果提示* 4.1 创建toast对象* 4.2 调用show方法->显示提示框*/
// 保存修改->点击
document.querySelector('.submit').addEventListener('click', () => {// 3.1 收集表单信息const userForm = document.querySelector('.user-form')const userObj = serialize(userForm, { hash: true, empty: true })userObj.creator = creator// 性别数字字符串,转成数字类型userObj.gender = +userObj.genderconsole.log(userObj)// 3.2 提交到服务器保存axios({url: 'http://hmajax.itheima.net/api/settings',method: 'PUT',data: userObj}).then(result => {// 4.1 创建toast对象const toastDom = document.querySelector('.my-toast')const toast = new bootstrap.Toast(toastDom)// 4.2 调用show方法->显示提示框toast.show()})
})
总结:bootstrap弹框什么使用js显示呢?
需要执行一些其他的js逻辑后,再去显示/隐藏弹框时。
四、知识点自测
1.什么方法可以将js数据类型改成json数据类型?
JSON.stringfy()
2.以下哪个方法,会延迟一段时间,再执行函数体,并执行一次就停止?
A、setTImeout(函数体,毫秒值)
B、setInterval(函数体,毫秒值)
选择A,
3.下列代码结果是多少?
A、true
B、大于
C、240
D、false
let obj = {
status: 240
}
const result = obj.status >= 200 && obj.status < 300
结果是Atrue
4.代码运行结果是多少?
let result = 'http://www.baidu.com'
result += '?a=10'
result += '&b=20'
A、http://www.baidu.com
B、?a=10
C、&b=20
D、http://www.baidu.com?a=10&b=20
5.以下哪个能实时监测到输入框值的变化?
A、input事件
B、change事件
选择A
五、XMLHttpRequest
1.基础使用
(1)AJAX是浏览器与服务器通信的技术,采用XMLHttpRequest对象的相关代码
(2)axios是对XHR相关的代码进行了封装,让我们只关心传递的接口参数
(3)学习XHR也是了解axios的内部与服务器交互过程的真正原理
语法如下:
const xhr = new XMLHttpRequest()
xhr.open('请求方法', '请求url网址')
xhr.addEventListener('loadend', () => {// 响应结果console.log(xhr.response)
})
xhr.send()
六、同步代码与异步代码
1.同步代码:
逐行执行,需要原地等待结果后,才能继续向下执行
2.异步代码:
调用后耗时,不阻塞代码继续执行(不必原地等待),在将来完成过后触发一个回调函数
const result = 0 + 1
console.log(result)
setTimeout(() => {
console.log(2)
}, 2000)
document.querySelector('.btn').addEventListener('click', () => {
console.log(3)
})
document.body.style.backgroundColor = 'pink'
console.log(4)
###结果是1,4,2,,点击一次按钮打印一次3
总结:(1)JS中有哪些异步代码?
setTimeout、setInterval、事件、Ajax
(2)异步代码用什么接受结果?
用回调函数
七、回调函数地狱
1.概念:
在回调函数中嵌套回调函数,一直嵌套下去就形成了回调函数地狱
2.缺点:
可读性差,异常无法捕获,耦合性严重,牵一发动全身
八、Promise-链式调用
1.概念:
依靠 then() 方法会返回一个新生成的 Promise 对象特性,继续串联下一环任务,直到结束
2.细节:
then() 回调函数中的返回值,会影响新生成的 Promise 对象最终状态和结果
3.优点:
通过链式调用,解决回调函数嵌套问题
总结:
(1)什么是Promise的链式调用?
使用then方法返回新的Promise对象特性,一直串联下去
(2)then回调函数,return的值回传递给那哪里?
传递给then方法生成的新Promise对象
(3)Promise链式调用有什么作用?
解决回调函数嵌套问题
九、解决回调函数地狱
方法:每个 Promise 对象中管理一个异步任务,用 then 返回 Promise 对象,串联起来
核心代码:
let pname = ''
// 1. 得到-获取省份Promise对象
axios({url: 'http://hmajax.itheima.net/api/province'}).then(result => {pname = result.data.list[0]document.querySelector('.province').innerHTML = pname// 2. 得到-获取城市Promise对象return axios({url: 'http://hmajax.itheima.net/api/city', params: { pname }})
}).then(result => {const cname = result.data.list[0]document.querySelector('.city').innerHTML = cname// 3. 得到-获取地区Promise对象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
})
十、async函数和await
1.概念:
在 async 函数内,使用 await 关键字取代 then 函数,等待获取 Promise 对象成功状态的结果值
2.方法:
使用 async 和 await 解决回调地狱问题
3.核心代码:
async function getData() {// 2. await等待Promise对象成功的结果const pObj = await axios({url: 'http://hmajax.itheima.net/api/province'})const pname = pObj.data.list[0]const cObj = await axios({url: 'http://hmajax.itheima.net/api/city', params: { pname }})const cname = cObj.data.list[0]const aObj = await axios({url: 'http://hmajax.itheima.net/api/area', params: { pname, cname }})const areaName = aObj.data.list[0]document.querySelector('.province').innerHTML = pnamedocument.querySelector('.city').innerHTML = cnamedocument.querySelector('.area').innerHTML = areaName
}getData()
十一、async函数和await捕获错误
1.try 和 catch 的作用:语句标记要尝试的语句块,并指定一个出现异常时抛出的响应
try {// 要执行的代码
} catch (error) {// error 接收的是,错误消息/
2.尝试把代码中 url 地址写错,运行观察 try catch 的捕获错误信息能力
总结:try和catch有什么作用?(捕获异常)
捕获同步流程的代码报错信息
十二、事件循环
1.概念:
执行代码和收集异步任务的模型,在调用栈空闲,反复调用任务队列里回调函数的执行机制,就叫事件循环
总结:
(1)什么是事件循环?
执行代码和收集异步任务,在调用栈空闲时,反复调用任务队列里的回调执行机制
(2)为什么有事件循环?
JavaScript是单线程的,为了不阻塞JS引擎,设计执行代码的模型
(3)JavaScript内代码是如何执行的?
执行同步代码,遇到异步代码交给宿主浏览器环境执行
十三、事件循环练习
console.log(1)
setTimeout(() => {console.log(2)
}, 0)
function myFn() {console.log(3)
}
function ajaxFn() {const xhr = new XMLHttpRequest()xhr.open('GET', 'http://hmajax.itheima.net/api/province')xhr.addEventListener('loadend', () => {console.log(4)})xhr.send()
}
for (let i = 0; i < 1; i++) {console.log(5)
}
ajaxFn()
document.addEventListener('click', () => {console.log(6)
})
myFn()
打印结果是?
1 5 3 2 4 点击一次document就会执行一次打印6
十四、宏任务与微任务
1.异步任务划分为了
宏任务:由浏览器环境执行的异步代码
微任务:由 JS 引擎环境执行的异步代码
2.输出结果
/*** 目标:阅读并回答打印的执行顺序
*/
console.log(1)
setTimeout(() => {console.log(2)
}, 0)
const p = new Promise((resolve, reject) => {resolve(3)
})
p.then(res => {console.log(res)
})
console.log(4)
注意:宏任务每次在执行同步代码时,产生微任务队列,清空微任务队列任务后,微任务队列空间释放!
下一次宏任务执行时,遇到微任务代码,才会再次申请微任务队列空间放入回调函数消息排队
即:一个宏任务包含微任务队列,他们之间是包含关系,不是并列关系
总结:
(1)什么是宏任务?
浏览器执行的异步代码 例如:JS 执行脚本事件,setTimeout/setInterval,AJAX请求完成事件,用户交互事件等
(2)什么是微任务?
JS 引擎执行的异步代码 例如:Promise对象.then()的回调
(3)JavaScript内代码如何执行?
执行第一个 script 脚本事件宏任务,里面同步代码 遇到 宏任务/微任务 交给宿主环境,有结果回调函数进入对应队列 当执行栈空闲时,清空微任务队列,再执行下一个宏任务,从1再来
十五、事件循环经典面试题目
// 目标:回答代码执行顺序
console.log(1)//1
setTimeout(() => {//宏任务console.log(2)//5const p = new Promise(resolve => resolve(3))//6(宏中微)p.then(result => console.log(result))
}, 0)
const p = new Promise(resolve => {setTimeout(() => {//宏任务console.log(4)//7}, 0)resolve(5)//3
})
p.then(result => console.log(result))//微任务
const p2 = new Promise(resolve => resolve(6))//4
p2.then(result => console.log(result))//微任务
console.log(7)//2
答案:1 7 5 6 2 3 4
十六、Promise.all静态方法
1.概念:
合并多个 Promise 对象,等待所有同时成功完成(或某一个失败),做后续逻辑
2.语法:
const p = Promise.all([Promise对象, Promise对象, ...])
p.then(result => {
// result 结果: [Promise对象成功结果, Promise对象成功结果, ...]
}).catch(error => {
// 第一个失败的 Promise 对象,抛出的异常对象
})
总结:
什么时候使用Promise.all ?
合并多个Promise对象并等待所有同时成功的结果,如果有一个报错就会最终为失败状态,当需要同时渲染多个接口数据同时到网页上使用。