文章目录
- Day1 Ajax入门
- 1.AJAX概念和axios的使用
- 2. 认识URL
- 3.URL的查询参数
- 4.常用的请求方法和数据提交
- 5.HTTP协议-报文
- 6.接口文档
- 7.form-serialize插件
- 8.案例用户登录
- Day2 Ajax综合案
- bootstrap弹框
- 图书管理
- 图片上传
- 更换背景
- 个人信息设置
- Day3 AJAX原理
- XMLHttpRequest
- Promise
- 封装简易版axios
- 案例-天气预报
- Day4 AJAX进阶
- 同步代码和异步代码
- 回调函数地狱和Promise链式调用
- async和await使用
- 事件循环(EventLoop)
- 宏任务与微任务-执行任务
- Promise.all静态方法
- 商品分类
- 学习反馈
- 总结
资料:
素材与资料都来自黑马程序员
思维导图
mdn网址
Day1 Ajax入门
1.AJAX概念和axios的使用
概念:AJAX是浏览器与服务器进行数据通信的技术
作用:浏览器和服务器之间通信,动态数据交互。
怎么使用AJAX?
1.先使用axios库,与服务器进行数据通信(使用场景:VUE,reat项目都会用到axios)
2.再学习XMLHttpREquest对象的使用,理解AJAX底层原理
axios使用:
- 1.引入axios库:
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
- 2.使用axios函数
√ 传入配置对象
√ 再用.then回调函数接收结果,并做后续的处理
axios({
url:'目标资源地址'
}).then(result => {
//对服务器进行后续的处理
})
2. 认识URL
URL:统一资源定位符,网址,用于访问服务器资源
解释url每个部分的作用:
http://
hmajax.itheima.net /api/login
协议
域名 资源路径
3.URL的查询参数
定义:浏览器提供给服务器额外信息,让服务器返回浏览器想要的数据
语法:http://xxxx.com/xxx/xxx?参数名1=值1&参数名2=值2
URL的查询参数的作用:浏览器提供给服务器额外信息,获取对应的数据
axios-查询参数:
使用params选项,携带参数名和值
axios在运行时把参数名和值,会拼接到url?参数名=值
axios({
url:'目标资源地址',
params:{
参数名:值
}).then(result => {
//对服务器进行后续的处理
})
4.常用的请求方法和数据提交
请求方法:对服务器资源,要执行的操作
请求方法 | 操作 |
---|---|
GET | 获取数据 |
POST | 数据提交 |
PUT | 修改数据(全部) |
DELETE | 删除数据 |
PATCH | 修改数据(部分) |
数据提交:当数据需要在服务器上保存
axios请求配置:
- url:请求的URL网址
- method:请求的方法,GET可以省略(不区分大小写)
- data:提交数据
axios({
url:'目标资源地址',
params:{
参数名:值
},
data:{
参数名:值
}
}).then(result => {
//对服务器进行后续的处理
})
axios-错误处理
axios({
//请求选项
}).then(result => {
//处理数据
}).catch(error => {
//处理错误
})
5.HTTP协议-报文
- 请求报文:浏览器发送给服务器的内容叫做请求报文
- 请求报文的组成:
1.请求行:请求方法,URL,协议
2.请求头:以键值对的格式携带的附加信息,比如Content-Type
3.空行:分隔请求头,空行之后的是发送给服务器的资源(感觉我的浏览器有点不一样)
4.请求体:发送的资源
- 通过Chrome的网络面板查看请求报文
响应报文的组成:
- 1.响应行:协议、Http响应状态码、状态信息
- 2.响应头:键值对的格式携带的附加信息,比如Content-Type
- 3.空行:分隔响应头,空行之后的是返回给浏览器的资源
- 4.响应体:返回的资源
Http响应状态码:用来表明请求是否成功完成。
状态码 | 说明 |
---|---|
1xx | 信息 |
2xx | 成功 |
3xx | 重定向消息 |
4xx | 客户端错误 |
5xx | 服务端错误 |
6.接口文档
接口文档:由后端提供的描述接口的文章
7.form-serialize插件
作用:快速收集表单元素的值。
素材:
<!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>13.form-serialize插件使用</title>
</head>
<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插件,快速收集表单元素值
-->
<script>
document.querySelector('.btn').addEventListener('click', () => {
})
</script>
</body>
</html>
<script src="./lib/form-serialize.js"></script>
<script>
document.querySelector('.btn').addEventListener('click', () => {
/*
2.使用serialize函数,快速收集表单元素的值
参数1:要获得哪个表单的数据
表单元素设置name属性,值会作为对象的属性名
建议name属性的值,最好和接口文档参数名一致
参数2:配置对象
hash:设置获取数据结构
-true:JS对象(推荐) 一般请求体里提交给服务器
-false;查询字符串
empty:设置是否获取空值
-true:获取空值(推荐)数据结构和标签结构一致
-false:不获得空值
*/
const form = document.querySelector('.example-form')
// const data = serialize(form,{hash:true,empty:true})
// const data = serialize(form,{hash:false,empty:true})
const data = serialize(form,{hash:true,empty:false})
console.log(data);
})
</script>
8.案例用户登录
素材:
<!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>12.案例_登录_提示消息</title>
<!-- 引入bootstrap.css -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css">
<!-- 公共 -->
<style>
html,
body {
background-color: #EDF0F5;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.container {
width: 520px;
height: 540px;
background-color: #fff;
padding: 60px;
box-sizing: border-box;
}
.container h3 {
font-weight: 900;
}
</style>
<!-- 表单容器和内容 -->
<style>
.form_wrap {
color: #8B929D !important;
}
.form-text {
color: #8B929D !important;
}
</style>
<!-- 提示框样式 -->
<style>
.alert {
transition: .5s;
opacity: 0;
}
.alert.show {
opacity: 1;
}
</style>
</head>
<body>
<div class="container">
<h3>欢迎-登录</h3>
<!-- 登录结果-提示框 -->
<div class="alert alert-success" role="alert">
提示消息
</div>
<!-- 表单 -->
<div class="form_wrap">
<form>
<div class="mb-3">
<label for="username" class="form-label">账号名</label>
<input type="text" class="form-control username">
</div>
<div class="mb-3">
<label for="password" class="form-label">密码</label>
<input type="password" class="form-control password">
</div>
<button type="button" class="btn btn-primary btn-login"> 登 录 </button>
</form>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
// 目标1:点击登录时,用户名和密码长度判断,并提交数据和服务器通信
// 目标2:使用提示框,反馈提示消息
// 1.1 登录-点击事件
document.querySelector('.btn-login').addEventListener('click', () => {
// 1.2 获取用户名和密码
const username = document.querySelector('.username').value
const password = document.querySelector('.password').value
// console.log(username, password)
// 1.3 判断长度
if (username.length < 8) {
console.log('用户名必须大于等于8位')
return // 阻止代码继续执行
}
if (password.length < 6) {
console.log('密码必须大于等于6位')
return // 阻止代码继续执行
}
// 1.4 基于axios提交用户名和密码
// console.log('提交数据到服务器')
axios({
url: 'http://hmajax.itheima.net/api/login',
method: 'POST',
data: {
username,
password
}
}).then(result => {
console.log(result)
console.log(result.data.message)
}).catch(error => {
console.log(error)
console.log(error.response.data.message)
})
})
</script>
</body>
</html>
<script>
// 目标1:点击登录时,用户名和密码长度判断,并提交数据和服务器通信
// 目标2:使用提示框,反馈提示消息
// 1.1 登录-点击事件
document.querySelector('.btn-login').addEventListener('click', () => {
// 1.2 获取用户名和密码
const username = document.querySelector('.username').value
const password = document.querySelector('.password').value
// console.log(username, password)
// 1.3 判断长度
if (username.length < 8) {
console.log('用户名必须大于等于8位')
return // 阻止代码继续执行
}
if (password.length < 6) {
console.log('密码必须大于等于6位')
return // 阻止代码继续执行
}
// 1.4 基于axios提交用户名和密码
// console.log('提交数据到服务器')
axios({
url: 'http://hmajax.itheima.net/api/login',
method: 'POST',
data: {
username,
password
}
}).then(result => {
console.log(result)
console.log(result.data.message)
}).catch(error => {
console.log(error)
console.log(error.response.data.message)
})
})
</script>
Day2 Ajax综合案
bootstrap弹框
bootstrap中文文档
- 属性控制方式
<!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>Bootstrap 弹框</title>
<!-- 引入bootstrap.css -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<!--
目标:使用Bootstrap弹框
1. 引入bootstrap.css 和 bootstrap.js
2. 准备弹框标签,确认结构
3. 通过自定义属性,控制弹框的显示和隐藏
-->
<button type="button" class="btn btn-primary" data-bs-target = '.my-box' data-bs-toggle="modal">
显示弹框
</button>
<!-- 弹框标签
bootstrap的标签modal弹框:添加modal类名(默认隐藏)
-->
<div class="modal my-box" tabindex="-1">
<div class="modal-dialog">
<!-- 弹框内容 -->
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Modal title</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>Modal body text goes here.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary">Save changes</button>
</div>
</div>
</div>
</div>
<!-- 引入bootstrap.js -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/bootstrap.min.js"></script>
</body>
</html>
- js属性控制
<!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>Bootstrap 弹框</title>
<!-- 引入bootstrap.css -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<!--
目标:使用JS控制弹框,显示和隐藏
1. 创建弹框对象
2. 调用弹框对象内置方法
.show() 显示
.hide() 隐藏
-->
<button type="button" class="btn btn-primary edit-btn">
编辑姓名
</button>
<div class="modal name-box" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">请输入姓名</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form action="">
<span>姓名:</span>
<input type="text" class="username">
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary save-btn">保存</button>
</div>
</div>
</div>
</div>
<!-- 引入bootstrap.js -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/bootstrap.min.js"></script>
<script>
// q.创建弹框对象
const modalDom = document.querySelector('.name-box')
const modal = new bootstrap.Modal(modalDom)
// 编辑姓名-->点击-->赋予默认姓名 -->弹框显示
document.querySelector('.edit-btn').addEventListener('click',()=>{
document.querySelector('.username').value = '默认姓名'
// 显示弹框
modal.show()
})
// 保存-->点击--->弹框隐藏
document.querySelector('.save-btn').addEventListener('click',()=>{
const username = document.querySelector('.username').value
console.log('模拟将姓名保存在服务器当中',username);
// 隐藏弹框
modal.hide()
})
</script>
</body>
</html>
图书管理
步骤如下:
- 目标1:渲染图书列表
1.1 获取数据
1.2 渲染数据
const creator = '老张'
// 封装-获取被渲染图书列表函数
function getBookList(){
// 1.1获取数据
axios({
url:'http://hmajax.itheima.net/api/books',
params:{
// 外号:获取对应数据
creator
}
}).then(result => {
// console.log(result);
// console.log(result.data.data);
const bookList = result.data.data
// 1.2渲染数据
const htmlStr = bookList.map((item,index) => {
return `
<tr>
<td>${index+1}</td>
<td>${item.bookname}</td>
<td>${item.author}</td>
<td>${item.publisher}</td>
<td data-id=${item.id}>
<span class="del">删除</span>
<span class="edit">编辑</span>
</td>
</tr>
`
}).join('')
// console.log(htmlStr);
document.querySelector('.list').innerHTML = htmlStr
})
}
getBookList()
- 目标2:新增图书
2.1新增弹框–>显示和隐藏
2.2 收集表单数据,并提交到服务器保存
2.3 刷新图示列表
// 2.1创建弹框对象
const addModalDom = document.querySelector('.add-modal')
const addModal =new bootstrap.Modal(addModalDom)
// 保存按钮->点击->隐藏弹框
document.querySelector('.add-btn').addEventListener('click',() => {
// 2.2 收集表单数据,并提交到服务器
const addForm = document.querySelector('.add-form')
const bookObj = serialize(addForm,{hash:true,empty:true})
console.log(bookObj);
// 提交到服务器
axios({
url:'http://hmajax.itheima.net/api/books',
method:'post',
data:{
...bookObj,
creator
}
}).then(result => {
console.log(result);
// 2.3添加成功之后,重新请求并渲染图书列表
getBookList()
// 重置表单
addForm.reset()
// 隐藏弹框
addModal.hide()
})
})
- 目标3: 删除图书
3.1 删除元素绑定点击事件 -> 获取图书id
3.2 调用删除接口
3.3刷新图书列表
// 3.1 删除元素绑定点击事件 -> 获取图书id
// 事件委托
document.querySelector('.list').addEventListener('click',e => {
// 获取触发事件目标元素
// 判断点击的是删除元素
if(e.target.classList.contains('del')){
// console.log('删除元素');
// 获取图书id(自定义属性)
const theId = e.target.parentNode.dataset.id
console.log(theId);
// 3.2 调用删除接口
axios({
url:`http://hmajax.itheima.net/api/books/${theId}`,
method:'delete'
}).then(() => {
// 3.3刷新图书列表
getBookList()
})
}
})
- 目标4:编辑图书
4.1 编辑弹框->显示和隐藏
4.2 获取当前编辑图书数据 -> 回显到编辑表单中
4.3 提交保存修改,并刷新列表
// 4.1 编辑弹框->显示和隐藏
const editDom = document.querySelector('.edit-modal')
const editModal = new bootstrap.Modal(editDom)
// 编辑元素 -> 点击弹框->弹框显示
document.querySelector('.list').addEventListener('click',e => {
// 判断点击的是否是编辑元素
if(e.target.classList.contains('edit')){
// 4.2 获取当前编辑图书数据 -> 回显到编辑表单中
const theId = e.target.parentNode.dataset.id
axios({
url:`http://hmajax.itheima.net/api/books/${theId}`
}).then((result) => {
const bookObj = result.data.data
// 数据对象“属性”和标签“类名”一致
// 遍历数据对象,使用属性去获得对应的标签,快速赋值
const keys = Object.keys(bookObj)
console.log(keys);// ['id', 'bookname', 'author', 'publisher']
keys.forEach(key => {
document.querySelector(`.edit-form .${key}`).value = bookObj[key]
})
})
editModal.show()
}
})
// 修改按钮->点击->隐藏弹框
document.querySelector('.edit-btn').addEventListener('click',()=>{
// 4.3 提交保存修改,并刷新列表
const editForm = document.querySelector('.edit-form')
const{id,bookname,author,publisher} = serialize(editForm,{hash: true,empty:true})
// 保存正在编辑的图书id,隐藏起来:无需用户修改
// <input type="hidden" class="id" name="id" value="230530">
axios({
url:`http://hmajax.itheima.net/api/books/${id}`,
method:'put',
data:{
bookname,author,publisher,creator
}
}).then(() => {
// 修改成功以后,重新1获取并刷新列表
getBookList()
})
editModal.hide()
})
图片上传
1. 获取图片文件
2. 使用 FormData 携带图片文件
3. 提交到服务器,获取图片url网址使用
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
// 文件选择元素->change事件
document.querySelector('.upload').addEventListener('change',e => {
// 1. 获取图片文件
// console.dir(e.target)
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
})
})
</script>
更换背景
目标:网站-更换背景
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])
// 提交到服务器,获取图片url网址使用
axios({
url:'http://hmajax.itheima.net/api/uploadimg',
method:'post',
data:fd
}).then(result => {
// console.dir(result);
const imgUrl = result.data.data.url
document.body.style.backgroundImage = `url(${imgUrl})`
// 2. 上传成功时,"保存"图片url网址
localStorage.setItem('bgImg',imgUrl)
})
})
// 网页运行后,"获取"url网址使用
const bgUrl = localStorage.getItem('bgImg')
console.log(bgUrl);
bgUrl && (document.body.style.backgroundImage = `url(${bgUrl})`)
个人信息设置
目标1:信息渲染
1.1 获取用户的数据
1.2 回显数据到标签上
const creator = '张三'
axios({
url:`http://hmajax.itheima.net/api/settings`,
params:{
creator
}
}).then(result => {
const userObj = result.data.data
console.log(userObj);
// 1.2回显数据到标签上
Object.keys(userObj).forEach(key => {
if(key === 'avatar'){
// 赋予默认头像
document.querySelector('.prew').src = userObj[key]
}else if(key === 'gender'){
// 赋予默认性别
// 获取性别单选框:[男radio元素,女radio元素]
const gRadioList = document.querySelectorAll('.gender')
// 获取性别数字:0男,1女
const gNum = userObj[key]
// 通过性别数字,作为下标,找到对应性别单选框,设置选中状态
gRadioList[gNum].checked = true
}else{
// 赋予默认值
document.querySelector(`.${key}`).value = userObj[key]
}
})
})
目标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 => {
console.log(result);
// data.data.avatar
const imgUrl = result.data.data.avatar
document.querySelector('.prew').src = imgUrl
})
})
/*
目标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.gender
console.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()
})
})
Day3 AJAX原理
XMLHttpRequest
XMLHttpRequest(XHR)
对象用于与服务器交互。通过 XMLHttpRequest 可以在不刷新页面的情况下请求特定 URL,获取数据。这允许网页在不影响用户操作的情况下,更新页面的局部内容。XMLHttpRequest 在 AJAX 编程中被大量使用。
AJAX原理:XMLHttpRequest对象;
学习XHR原因:
1.有更多与服务器数据通信方式
2.了解axios内部原理
XHR使用步骤:
- 创建XHR对象
- 调用open方法,设置url和请求方法
- 监听loadend事件,接收结果
- 调用send方法,发起请求
let xhr = new XMLHttpRequest()
xhr.open('请求方法','请求url网址')
xhr.addEventlistener('loadend',()=>{
//响应结果
console.log(xhr.response) 接收响应
})
xhr.send() 发送请求
Promise
概念:表示(管理)一个异步操作最终状态和结果值的对象
好处:
1.逻辑清晰
2.了解axios函数内部运作机制
3.能解决回调函数地狱问题
学习promise对象原因:
√ 成功和失败状态,可以管理对于处理程序
√ 了解axios内部原理
promise使用步骤:
<script>
/**
* 目标:使用Promise管理异步任务
*/
// 1.创建promise对象(pending待定状态)
const p = new Promise((resolve, reject) => {
// Promise对象创建,这里的代码都会执行
// 2.执行代码
setTimeout(() => {
// resolve()=>'fulfilled状态--已兑现'=> then()
resolve('模拟AJAX请求-成功结果')
// reject()=>'reject状态--已拒绝'=> catch
()
reject(new Error('模拟AJAX请求-失败结果'))
}, 2000)
})
console.log(p);
// 3.获取结果
p.then(result => {
console.log(result);
}).catch(error => {
console.log(error);
})
</script>
目标:使用Promise管理XHR请求省份列表
1. 创建Promise对象
2. 执行XHR异步代码,获取省份列表
3. 关联成功或失败函数,做后续处理
<script>
// 1. 创建Promise对象
const p = new Promise((resolve,reject)=> {
// 2. 执行XHR异步代码,获取省份列表
const xhr = new XMLHttpRequest()
xhr.open('GET','http://hmajax.itheima.net/api/province')
xhr.addEventListener('loadend',()=>{
// xhr如何判断响应成功还是失败的?
// 2xx开头的都是成功响应状态码
console.log(xhr);
if(xhr.status >= 200 && xhr.status < 300){
resolve(JSON.parse(xhr.response))
}else{
reject(new Error(xhr.response))
}
})
xhr.send()
})
//3. 关联成功或失败函数,做后续处理
p.then((result)=>{
console.log(result);
document.querySelector('.my-p').innerHTML = result.list.join('<br>')
}).catch(error=>{
// 错误对象要用console.dir详细打印
console.dir(error);
// 服务器返回错误提示消息,插入p标签
document.querySelector('.my-p').innerHTML = error.message
})
</script>
封装简易版axios
目标:封装_简易axios函数_获取省份列表
1. 定义myAxios函数,接收配置对象,返回Promise对象
2. 发起XHR请求,默认请求方法为GET
3. 调用成功/失败的处理程序
4. 使用myAxios函数,获取省份列表展示
<script>
// 1. 定义myAxios函数,接收配置对象,返回Promise对象
function myAxios(config){
return new Promise((resolve,reject) =>{
// 2. 发起XHR请求,默认请求方法为GET
const xhr = new XMLHttpRequest()
xhr.open('GET'||config.method,config.url)
// 3. 调用成功/失败的处理程序
xhr.addEventListener('loadend',()=>{
if(xhr.status >= 200 && xhr.status < 300){
resolve(JSON.parse(xhr.response))
}else{
reject(new Error(xhr.response))
}
})
xhr.send()
})
}
myAxios({
url:'http://hmajax.itheima.net/api/province'
}).then(result => {
console.log(result);
document.querySelector('.my-p').innerHTML = result.list.join('<br>')
}).catch(error => {
console.dir(error);
document.querySelector('.my-p').innerHTML = error.message
})
</script>
目标:封装_简易axios函数_获取地区列表
1. 判断有params选项,携带查询参数
2. 使用URLSearchParams转换,并携带到url上
3. 使用myAxios函数,获取地区列表
<script>
function myAxios(config) {
if(config.params){
const paramsObj = new URLSearchParams(config.params)
const queryString = paramsObj.toString()
config.url = config.url+`?${queryString}`
}
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.open(config.method || 'GET', config.url)
xhr.addEventListener('loadend', () => {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.response))
} else {
reject(new Error(xhr.response))
}
})
xhr.send()
})
}
myAxios({
url:'http://hmajax.itheima.net/api/area',
params:{
pname:'广东省',
cname:'河源市'
}
}).then((result)=>{
console.log(result);
document.querySelector('.my-p').innerHTML = result.list.join('<br>')
}).catch(error=>{
// 错误对象要用console.dir详细打印
console.dir(error);
// 服务器返回错误提示消息,插入p标签
document.querySelector('.my-p').innerHTML = error.message
})
</script>
目标:封装_简易axios函数_注册用户
1. 判断有data选项,携带请求体
2. 转换数据类型,在send中发送
3. 使用myAxios函数,完成注册用户
<script>
function myAxios(config) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
if (config.params) {
const paramsObj = new URLSearchParams(config.params)
const queryString = paramsObj.toString()
config.url += `?${queryString}`
}
xhr.open(config.method || 'GET', config.url)
xhr.addEventListener('loadend', () => {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.response))
} else {
reject(new Error(xhr.response))
}
})
if(config.data){
// 告诉服务器,我传递的内容类型是JSON字符串
xhr.setRequestHeader('Content-Type','application/json')
// 准备数据并转成JSON字符串
xhr.send(JSON.stringify(config.data))
}else{
xhr.send()
}
})
}
document.querySelector('.reg-btn').addEventListener('click',() =>{
myAxios({
url:'http://hmajax.itheima.net/api/register',
method:'POST',
data:{
username:'zz111111111222',
password:'12345111226'
}
}).then((result)=>{
console.log(result);
// document.querySelector('.my-p').innerHTML = result.list.join('<br>')
}).catch(error=>{
// 错误对象要用console.dir详细打印
console.dir(error);
// 服务器返回错误提示消息,插入p标签
// document.querySelector('.my-p').innerHTML = error.message
})
})
</script>
案例-天气预报
目标1:默认显示-北京市天气
1.1 获取北京市天气数据
1.2 数据展示到页面
// 获取并渲染城市天气函数
function getWeather(cityCode){
// 1.1获取北京市天气数据
myAxios({
url:'http://hmajax.itheima.net/api/weather',
params:{
city:cityCode
}
}).then(result => {
console.log(result);
const wObj = result.data
// 1.2数据展示到页面
const dateStr = ` <span class="dateShort">${wObj.date}</span>
<span class="calendar">农历
<span class="dateLunar">${wObj.dateLunar}</span>`
document.querySelector('.title').innerHTML=dateStr
// 城市名字
document.querySelector('.area').innerHTML = wObj.area
// 当天天气
const nowWStr = `
<div class="tem-box">
<span class="temp">
<span class="temperature">${wObj.temperature}</span>
<span>°</span>
</span>
</div>
<div class="climate-box">
<div class="air">
<span class="psPm25">${wObj.psPm25}</span>
<span class="psPm25Level">${wObj.psPm25Level}</span>
</div>
<ul class="weather-list">
<li>
<img src=${wObj.weatherImg} class="weatherImg" alt="">
<span class="weather">${wObj.weather}</span>
</li>
<li class="windDirection">${wObj.windDirection}</li>
<li class="windPower">${wObj.windPower}</li>
</ul>
</div>`
document.querySelector('.weather-box').innerHTML = nowWStr
// 当前天气
const twObj = wObj.todayWeather
console.log(twObj);
const todayWStr = `
<div class="range-box">
<span>今天:</span>
<span class="range">
<span class="weather">${twObj.weather}</span>
<span class="temNight">${twObj.temNight}</span>
<span>-</span>
<span class="temDay">${twObj.temDay}</span>
<span>℃</span>
</span>
</div>
<ul class="sun-list">
<li>
<span>紫外线</span>
<span class="ultraviolet">${twObj.ultraviolet}</span>
</li>
<li>
<span>湿度</span>
<span class="humidity">${twObj.humidity}</span>%
</li>
<li>
<span>日出</span>
<span class="sunriseTime">${twObj.sunriseTime}</span>
</li>
<li>
<span>日落</span>
<span class="sunsetTime">${twObj.sunsetTime}</span>
</li>
</ul>`
document.querySelector('.today-weather').innerHTML = todayWStr
// 7天天气预报
const dayForecast = wObj.dayForecast
const dayForecastStr = dayForecast.map(item =>{
return `<li class="item">
<div class="date-box">
<span class="dateFormat">${item.dateFormat}</span>
<span class="date">${item.date}</span>
</div>
<img src="${item.weatherImg}" alt="" class="weatherImg">
<span class="weather">${item.weather}</span>
<div class="temp">
<span class="temNight">${item.temNight}</span>-
<span class="temDay">${item.temDay}</span>
<span>℃</span>
</div>
<div class="wind">
<span class="windDirection">${item.windDirection}</span>
<span class="windPower"><${item.windPower}</span>
</div>
</li>`
}).join('')
document.querySelector('.week-wrap').innerHTML=dayForecastStr
}).catch(error =>{
console.log(error);
})
}
//默认进入网页-就要获取天气数据
getWeather('110100')
目标2:搜索城市列表
2.1绑定input手机开你,获取关键字
2.2获取城市列表数据
// 2.1绑定input手机开你,获取关键字
document.querySelector('.search-city').addEventListener('input',e => {
// 2.2获取城市列表数据
console.log(e.target.value);
myAxios({
url:'http://hmajax.itheima.net/api/weather/city',
params:{
city: e.target.value
}
}).then(result => {
console.log(result);
const liStr = result.data.map(item => {
return `<li class="city-item" data-code=${item.code}>${item.name}</li>`
}).join('')
console.log(liStr);
const a =document.querySelector('.search-list').innerHTML = liStr
console.log(a);
}).catch(error => {
console.dir(error)
})
})
目标3:切换城市天气
3.1绑定城市点击事件,获取城市code值
3.2 调用获取并展示天气的函数
document.querySelector('.search-list').addEventListener('click',e => {
if(e.target.tagName === 'LI'){
getWeather(e.target.dataset.code)
}
})
Day4 AJAX进阶
同步代码和异步代码
同步代码:逐行执行,需原地等待结果后,才继续向下执行
异步代码:调用后耗时,不阻塞代码继续执行(不必原地等待),在将来完成后触发一个回调函数。
Js中的异步代码:setTimeout/setInterval,事件,AJAX
回调函数地狱和Promise链式调用
1.回调函数地狱
在回调函数一直向下嵌套回调函数,形成回调函数地狱
目标:演示回调函数地狱
需求:获取默认第一个省,第一个市,第一个地区并展示在下拉菜单中
概念:在回调函数中嵌套回调函数,一直嵌套下去就形成了回调函数地狱
缺点:可读性差,异常无法获取,耦合性严重,牵一发动全身
<script>
axios({url:'http://hmajax.itheima.net/api/province'}).then(result => {
const pname = result.data.list[0]
document.querySelector('.province').innerHTML = pname
// 2.获取默认第一个城市的名字
axios({url:'http://hmajax.itheima.net/api/city', params:{pname}}).then(result => {
const cname = result.data.list[0]
document.querySelector('.city').innerHTML = cname
// 3.获取默认第一个地区的名字
axios({url:'http://hmajax.itheima.net/api/area',params:{pname,cname}}).then(result => {
const areaName = result.data.list[0]
document.querySelector('.area').innerHTML = areaName
})
})
})
</script>
2.Promise的链式调用–入门
概念:依靠then()方法会返回一个新生成的Promise对象特性,继续初恋下一环任务,直到结束
细节:then()回调函数中的返回值,会影响新生成的Promise对象最终状态和结果
目标:掌握Promise的链式调用
需求:把省市的嵌套结构,改成链式调用的线性结构
<script>
// 1.创建Promise对象-模拟请求省份名字
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('北京市')
}, 2000)
})
// 2.获取省份的名字
const p2 = p.then(result => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(result + '-----北京市')
}, 2000)
})
})
// 4.获取城市名字
p2.then(result => {
console.log(result);
})
</script>
3.解决回调函数地狱
目标:把回调函数嵌套代码,改成Promise链式调用结构
需求:获取默认第一个省,第一个市,第一个地区并展示在下拉菜单中
<script>
let pname = ''
axios({url:'http://hmajax.itheima.net/api/province'}).then(result => {
pname = result.data.list[0]
document.querySelector('.province').innerHTML = pname
// 2.获取默认第一个城市的名字
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.获取默认第一个地区的名字
return axios({url:'http://hmajax.itheima.net/api/area',params:{pname,cname}}).then(result => {
const areaName = result.data.list[0]
document.querySelector('.area').innerHTML = areaName
})
})
})
async和await使用
概念;在async函数内,使用await关键字取代then函数,等待获取Promise对象成功状态的结果值
async函数和await捕获错误:使用 try…catch…
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
/**
* 目标:掌握async和await语法,解决回调函数地狱
* 概念:在async函数内,使用await关键字,获取Promise对象"成功状态"结果值
* 注意:await必须用在async修饰的函数内(await会阻止"异步函数内"代码继续执行,原地等待结果)
*/
// 1.定义async修饰函数
async function getData(){
try {
/*要执行的代码*/
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/area1',params:{pname,cname}})
const areaName = aObj.data.list[0]
document.querySelector('.province').innerHTML = pname
document.querySelector('.city').innerHTML = cname
document.querySelector('.area').innerHTML = areaName
} catch (error) {
/* error接收的是,错误信息
try里代码,如果有错误,直接进入这里执行*/
console.dir(error);
}
}
getData()
</script>
事件循环(EventLoop)
概念:JavaScript 有一个基于事件循环的并发模型,事件循环负责执行代码、收集和处理事件以及执行队列中的子任务。这个模型与其他语言中的模型截然不同,比如 C 和 Java。
原因;JavaScript单线程(某一时刻只能执行一行代码),为了让耗时代码不阻塞其他代码运行,设计了事件循环模型。
JavaScript内代码如何执行?
1.执行同步代码,遇到异步代码交给宿主浏览器环境执行
2.异步有了结果后,把回调函数放入任务队列排队
3.当调用栈空闲后,反复调用任务队列里的回调函数
<script>
/**
* 目标:阅读并回答执行的顺序结果
*/
console.log(1)
setTimeout(() => {
console.log(2)
}, 0)
console.log(3)
setTimeout(() => {
console.log(4)
}, 2000)
console.log(5)
</script>
事件循环练习
<script>
/**
* 目标:阅读并回答执行的顺序结果
* 1 5 3 2 4 6(点击时触发)
*/
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()
</script>
事件·循环·练习
宏任务与微任务-执行任务
宏任务
:浏览器执行的异步代码,例如:JS执行脚本事件,setTimeout/setInterval,AJAX请求完成事件,用户交互事件等。
微任务
:JS引擎执行的异步代码,例如:Promise对象.then()的回调
JavaScript内代码如何执行?
1.执行第一个script脚本时间宏任务,里面同步代码
2.遇到宏任务/微任务交给宿主环境,有结果回调函数进入对应队列
当执行栈空闲时,清空微任务队列,再执行下一个宏任务,从1再来
<script>
// 目标:回答代码执行顺序
console.log(1)
setTimeout(() => {
console.log(2)
const p = new Promise(resolve => resolve(3))
p.then(result => console.log(result))
}, 0)
const p = new Promise(resolve => {
setTimeout(() => {
console.log(4)
}, 0)
resolve(5)
})
p.then(result => console.log(result))
const p2 = new Promise(resolve => resolve(6))
p2.then(result => console.log(result))
console.log(7)
</script>
事件循环-执行过程
Promise.all静态方法
概念:合并多个Promise对象,等待所有提示成功完成(或某一个失败),做后续逻辑
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
/**
* 目标:掌握Promise的all方法作用,和使用场景
* 业务:当我需要同一时间显示多个请求的结果时,就要把多请求合并
* 例如:默认显示"北京", "上海", "广州", "深圳"的天气在首页查看
* code:
* 北京-110100
* 上海-310100
* 广州-440100
* 深圳-440300
*/
// 1.请求城市天气,得到Promise对象
const bjObj = axios({url:'http://hmajax.itheima.net/api/weather',params:{city:'110100'}})
const shObj = axios({url:'http://hmajax.itheima.net/api/weather',params:{city:'310100'}})
const gzObj = axios({url:'http://hmajax.itheima.net/api/weather',params:{city:'440100'}})
const szObj = axios({url:'http://hmajax.itheima.net/api/weather',params:{city:'440300'}})
// console.log(bjObj,shObj, gzObj,szObj);
// 2.使用 Promise.all,合并对各Promise对象
const p = Promise.all([bjObj,shObj, gzObj,szObj])
p.then(result => {
console.log(result);
// 注意:结果数组顺序和合并时顺序是一致的
const htmlStr = result.map(item => {
// 解构赋值
const {area,weather}=item.data.data
return `<li>${area}-----${weather}</li>`
}).join('')
document.querySelector('.my-ul').innerHTML = htmlStr
}).catch(error => {
console.dir(eerror)
})
</script>
商品分类
目标:把所有商品分类“同时”渲染到页面上
1. 获取所有一级分类数据
2. 遍历id,创建获取二级分类请求
3. 合并所有二级分类Promise对象
4. 等待同时成功后,渲染页面
<script>
// 1. 获取所有一级分类数据
axios({url:'http://hmajax.itheima.net/api/category/top'}).then(result => {
console.log(result);
// 2. 遍历id,创建获取二级分类请求
const promiseList = result.data.data.map(item =>{
return axios({url:'http://hmajax.itheima.net/api/category/sub',params:{id:item.id}})
})
console.log(promiseList);
// 3. 合并所有二级分类Promise对象
const p = Promise.all(promiseList)
p.then(result => {
console.log(result);
// 4. 等待同时成功后,渲染页面
const htmlStr = result.map(item => {
const obj = item.data.data
return`<div class="item">
<h3>一级分类名字:${obj.name}</h3>
<ul>
${obj.children.map(item => {
return ` <li>
<a href="javascript:;">
<img src=${item.picture}">
<p>${item.name}</p>
</a>
</li>`
}).join('')}
</ul>
</div>`
}).join('')
document.querySelector('.sub-list').innerHTML = htmlStr
})
})
</script>
学习反馈
目标1:完成省市区下拉列表切换
1.1 设置省份下拉菜单数据
1.2 切换省份,设置城市下拉菜单数据,清空地区下拉菜单
1.3 切换城市,设置地区下拉菜单数据
目标2:收集数据保存
2.1 监听提交的点击事件
2.2 依靠插件收集表单数据
2.3 基于axios提交保存
/**
* 目标1:完成省市区下拉列表切换
* 1.1 设置省份下拉菜单数据
* 1.2 切换省份,设置城市下拉菜单数据,清空地区下拉菜单
* 1.3 切换城市,设置地区下拉菜单数据
*/
// 1.1 设置省份下拉菜单数据
axios({url:'http://hmajax.itheima.net/api/province'}).then(result => {
const optionStr = result.data.list.map(item => {
return ` <option value="${item}">${item}</option>`
}).join('')
document.querySelector('.province').innerHTML=`<option value="">省份</option>`+optionStr
})
// 1.2 切换省份,设置城市下拉菜单数据,清空地区下拉菜单
document.querySelector('.province').addEventListener('change',async e => {
// 获取用户选择的省份名字
console.log(e.target.value);
const result = await axios({url:'http://hmajax.itheima.net/api/city',params:{pname:e.target.value}}).then(result =>{
const optionStr = result.data.list.map(item => {
return ` <option value="${item}">${item}</option>`
}).join('')
// 把默认初始选项+下属城市数据插入select中
document.querySelector('.city').innerHTML=`<option value="">城市</option>`+optionStr
// 清空地区数据
document.querySelector('.area').innerHTML= `<option value="">地区</option>`
})
})
// 1.3 切换城市,设置地区下拉菜单数据
document.querySelector('.city').addEventListener('change',async e => {
console.log(e.target.value);
const result = await axios({url:'http://hmajax.itheima.net/api/area',
params:{pname: document.querySelector('.province').value,
cname:e.target.value}}).then(result =>{
const optionStr = result.data.list.map(item => {
return ` <option value="${item}">${item}</option>`
}).join('')
document.querySelector('.area').innerHTML=`<option value="">地区</option>`+optionStr
})
})
/*
目标2:收集数据保存
2.1 监听提交的点击事件
2.2 依靠插件收集表单数据
2.3 基于axios提交保存
*/
// 2.1 监听提交的点击事件
document.querySelector('.submit').addEventListener('click',async ()=> {
// 2.2 依靠插件收集表单数据
const form =document.querySelector('.info-form')
const data = serialize(form,{hash:true,empty:true})
console.log(data);
// 2.3 基于axios提交保存
try {
const result = await axios({
url:'http://hmajax.itheima.net/api/feedback',
method:'post',
data
})
console.log(result);
alert(result.data.message)
} catch (error) {
console.dir(error);
alert(error.response.data.message)
}
})
总结
学习了这门课程,AJAX的大概知识我都清楚了,也在学习过程中,我得到了一些学习经验,比如,看完一天的视频,要回去复习,不然容易忘记,边看视频边敲代码边理解或者看完一天视频再敲代码,可能带着回忆的状态,这样子可以检验自己是否真的懂(可能这个方法会好一点,因人而异),还有就是代码也不是敲一遍就行的,最多敲三遍,最后一遍一定是自己明白了,在不借助外力的情况下,自己敲(虽然但是,我还没达到那种境界,在努力中)。
最后送大家一句话:博学之,审问之,慎思之,明辨之,笃行之。