AJAX入门
让数据活起来
ajax概念和axios使用
什么是AJAX
ajax是浏览器和服务器进行数据通信的技术
怎么用ajax
1.先使用axios库,与服务器进行数据通信 axios:是基于XMLHttpPequest封装,代码简单 2.再学习XMLHttpRequest对象的使用,了解ajax底层原理
axios使用
语法: 1.引入axios.js:https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js 2.使用axios 函数
认识 URL
知道url作用和组成,方便和后端人员沟通
什么是url
定义:统一资源定位符,或统一资源定位器,定位地址,url地址,简称网址,访问网络上的资源
url的组成
http协议:超文本传输协议,规定浏览器和服务器之间传输数据的格式
域名:标记服务器在互联网中方位
资源路径:标记资源在服务器下的具体位置
获取新闻列表
需求:使用axios从服务器拿到新闻列表的数据
目标资源地址hmajax.itheima.net/api/news
<!--
新闻数据地址: http://hmajax.itheima.net/api/news
-->
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
axios({
url:' http://hmajax.itheima.net/api/news'
}).then(result=>{
console.log(result)
})
</script>
URL 参数查询
定义:浏览器提供给服务器的额外信息,让服务器返回浏览器想要的数据
语法:语法:xxx.com/xxx/xxx? 参数名 1= 值 1 & 参数名 2= 值 2
axios查询参数
语法:使用 axios提供的params选项
注意:axios在运行时把参数名和值会拼接到url的?后面,参数名=值
地区查询案例
需求: 根据输入的省份名字和城市名字,查询地区并且渲染列表
过程:确定url网址和参数说明
/*
获取地区列表: http://hmajax.itheima.net/api/area
查询参数:
pname: 省份或直辖市名字
cname: 城市名字
*/
// 1.获取用户输入的省份和城市的名字 错了,先要写一个绑定点击事件
//1,绑定点击事件
document.querySelector('.btn').addEventListener('click',()=>{
let pname=document.querySelector('.province').value
let cname=document.querySelector('.city').value
axios({
url:'http://hmajax.itheima.net/api/area',
params: {
pname:pname,
cname:cname
}
}).then(result=>{
// console.log(result)
const li=result.data.list
const list = li.map( areaname=>
`<li class="list-group-item">${areaname}</li>`).join('')
// console.log(list)
//到这里就成功拿到数据了,把成功拼接好的数据渲染到页面中指定的盒子里面
document.querySelector('.list-group-item').innerHTML=list
})
})
常用请求方法和数据提交
请求方法:对服务器资源,要执行的操作
####3 axios请求配置 url:请求的url网址 method:请求的方法,get可以省略 data:数据提交
注册账号案例
需求:通过axios提交用户名和密码,完成注册功能
<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:'yanqianrui',
password:'1223456'
}
}).then(result=>{
console.log(result)
})
})
axios错误处理
语法:在then的后面,通过.catch方法,传入回调函数并定义形参
HTTP协议-报文
http协议:规定了浏览器发送及服务器返回内容的格式
请求报文:浏览器按照http协议要求的格式,发送给服务器的内容
请求报文的格式
请求报文的组成部分有:
- 请求行:请求方法,url,协议
- 请求头:以键值对的格式携带的附加信息,比如:contene-type
- 空行:分隔请求头,空行之后是发送给服务器的资源
- 请求体:发送给服务器的资源
响应报文
服务器按照http协议要求的格式,返回给浏览器的内容
- 响应行(状态行):协议,http响应状态码,状态信息
- 其余同请求报文
响应状态码
接口文档
接口文档: 描述接口的文章(后端工程师)
接口:使用ajax和服务器进行通讯时,使用的url,请求方法,以及参数
黑马ajax接口文档:传送门
案例-用户登录
- 点击登陆时,判断用户名和密码长度
- 提交数据和服务器通信
- 提示信息
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
// 目标1:点击登录时,用户名和密码长度判断,并提交数据和服务器通信
// 目标2:使用提示框,反馈提示消息
//提示框事件
//因为多个地方需要调用提示框功能,所以需要封装一个函数以便随时调用
const n=document.querySelector('.alert')
function myalert(msg,ifsuccess){
//第一步调用函数的时候开启提示框
n.classList.add('show')
//第二步,显示对应的信息
n.innerText=msg
const x=ifsuccess?'alert-success':'alert-danger'
n.classList.add(x)
//第三步,两秒之后关闭提示框
setTimeout(()=>{
n.classList.remove(x)
n.classList.remove('show')
},2000)
}
// 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位')
myalert('用户名必须大于等于8位',false)
return // 阻止代码继续执行
}
if (password.length < 6) {
console.log('密码必须大于等于6位')
myalert('密码必须大于等于6位',false)
return // 阻止代码继续执行
}
// 1.4 基于axios提交用户名和密码
// console.log('提交数据到服务器')
axios({
url: 'http://hmajax.itheima.net/api/login',
method: 'POST',
data: {
username,
password
}
}).then(result => {
myalert(result.data.message,true)
console.log(result)
console.log(result.data.message)
}).catch(error => {
myalert(error.response.data.message,false)
console.log(error)
console.log(error.response.data.message)
})
})
</script>
form-serialize插件
作用:快速收集表单元素的值
语法const data =serialize(form,{hash:true,empty:true})
用户登录案例
// 1.1 登录-点击事件
document.querySelector('.btn-login').addEventListener('click', () => {
//使用插件来获取表单中的值
const form=document.querySelector('.form-1')
const data=serialize(form,{hash:true,empty:true})
console.log(data)
const {username,password}=data
AJAX综合案例
图书管理案例
bootstrap 弹框
功能:不离开当前页面,显示单独内容,供用户操作
步骤:
- 引入bootstrap.css和bootstrap.js
- 准备弹框标签,确认结构
- 通过自定义属性,控制弹框的显示和隐藏(单纯显示隐藏)
<!--
目标:使用Bootstrap弹框
1. 引入bootstrap.css 和 bootstrap.js
2. 准备弹框标签,确认结构
3. 通过自定义属性,控制弹框的显示和隐藏
-->
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target=".my-box">
显示弹框
</button>
- 也可以通过js控制弹框的显示和隐藏(额外逻辑代码)
<!-- 引入bootstrap.js -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/bootstrap.min.js"></script>
<script>
//1.创建弹框对象
const modalDom=document.querySelector('.name-box')
const modal= new bootstrap.Modal(modalDom)
//编辑姓名--点击--弹框显示
document.querySelector('.edit-btn').addEventListener('click',()=>{
document.querySelector('.username').value='默认姓名'
//2.显示弹框
modal.show()
})
//保存---点击--弹框隐藏
document.querySelector('.save-btn').addEventListener('click',()=>{
const username=document.querySelector('.username').value
console.log('模拟把姓名保存到服务器上',username)
//隐藏弹框
modal.hide()
})
</script>
渲染列表(查)
自己的图书数据:给自己起个外号,并告诉服务器,默认会有三本书,基于这些做数据的增删改查
过程:获取数据--->渲染数据
/**
* 目标1:渲染图书列表
* 1.1 获取数据
* 1.2 渲染数据
*/
//封装一个 获取并渲染图书列表的函数
const creator='张三'
function getBooksList(){
//1.1获取数据
axios({
url:'http://hmajax.itheima.net/api/books',
//method可以省略不写,因为默认就是get请求
params:{
creator
}
}).then(result=>{
// console.log(result)
const booklist=result.data.data
const data=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('')
document.querySelector('.list').innerHTML= data
})
}
getBooksList()
新增图书(增)
过程:新增图书-弹框(显示或者隐藏)-->收集数据&提交保存-->刷新-图书列表
//目标二:新增图书
//新增弹框:显示和隐藏
//收集表单数据,并提交到服务器保存
//重新渲染新数据
//保存按钮--点击--隐藏弹出框
const mymodal=document.querySelector('.add-modal')
const modal=new bootstrap.Modal(mymodal)
document.querySelector('.add-btn').addEventListener('click',()=>{
//隐藏弹框
modal.hide()
//收集表单数据,并提交到服务器保存
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:creator
}
}).then(result=>{
//重新渲染页面,直接调用封装好的函数
getBooksList()
//重置表单
addForm.reset()
})
})
删除图书(删)
过程:绑定点击事件(获取图书id)-->调用删除接口-->刷新图书列表
//目标三:删除图书
//绑定事件-->获取图书id
//调用删除接口
//刷新图书列表
//3.1删除元素-->点击(事件委托)
document.querySelector('.list').addEventListener('click',e=>{
//获取触发事件的目标元素
// console.log(e.target)
//判断点击的是删除元素
if(e.target.classList.contains('del')){
//判断是否点击了删除元素
// console.log('点击了删除元素')
//获取图书id(自定义属性id)
const theid=e.target.parentNode.dataset.id
// console.log(theid)
axios({
url:`http://hmajax.itheima.net/api/books/${theid}`,
method:'DELETE'
}).then(result=>{
getBooksList()
})
}
})
编辑图书(改)
过程:编辑图书-弹框(显示或隐藏)--表单(数据回显)--保存修改&刷新列表
//功能4,编辑功能
//编辑弹框-->显示和隐藏
//获取当前编辑图书数据-->回显到编辑表单中
//提交保存修改,并刷新列表
//编辑弹框--->显示和隐藏()
const dommodal=document.querySelector('.edit-modal')
const editmodal=new bootstrap.Modal(dommodal)
//编辑元素--点击--弹框显示
document.querySelector('.list').addEventListener('click',e=>{
if(e.target.classList.contains('edit')){
//获取当前编辑的图书数据-->回显到编辑表单中
const theiid=e.target.parentNode.dataset.id
axios({
url:`http://hmajax.itheima.net/api/books/${theiid}`,
}).then(result=>{
// console.log(result)
const bookobj=result.data.data
const keys=Object.keys(bookobj)//批量拿到数据中的属性以及其value值
//遍历数据对象,使用属性去获取对应的标签
keys.forEach(key=>{
document.querySelector(`.edit-form .${key}`).value=bookobj[key]
})
})
editmodal.show()
}
})
//点击修改按钮,重新获取表单中的数据,然后重新渲染在列表中
document.querySelector('.edit-btn').addEventListener('click',e=>{
const editForm=document.querySelector('.edit-form')
const {id,bookname,author,publisher}=serialize(editForm,{hash:true,empty:true})
//发送请求修改服务器中的数据
axios({
//接口文档中要求传入很多个参数,有bookname等,所以返回前面,使用解构赋值把参数解构出来
url:`http://hmajax.itheima.net/api/books/${id}`,
method:'PUT',
data:{
bookname,
author,
publisher,
creator
}
}).then(()=>{
//服务器中数据修改完毕,调用封装好的函数渲染列表
getBooksList()
//等数据正确修改之后弹框才能关闭
editmodal.hide()
})
})
图片上传
- 获取图片文件对象
- 使用formdata携带图片文件
3. 提交保单数据到服务器,使用图片url网址
<!-- 文件选择元素 -->
<input type="file" class="upload">
<img src="" alt="" class="imggg">
<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=>{
//获取图片文件成功
console.log(e.target.files[0])
//提交到服务器保存
const fd=new FormData()
const ss=fd.append('img',e.target.files[0])
axios({
url:'http://hmajax.itheima.net/api/uploadimg',
method:'POST',
data:fd
}).then(result=>{
console.log(result)
//获取到了图片信息,然后用url属性显示到网页中
//1.获取图片在服务器中存储的url地址
const imgurl=result.data.data.url
//这里需要再回去创建一个img的盒子
document.querySelector('.imggg').src= imgurl
})
})
</script>
网站换肤案例
网站更换背景
- 选择图片上传,设置body背景
- 上传成功时,保存url网址
- 网页运行后,获取url网址使用
/**
* 目标:网站-更换背景
* 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()
const bgcimg=fd.append('img',e.target.files[0])
axios({
url:'http://hmajax.itheima.net/api/uploadimg',
method:'POST',
data:fd
}).then(result=>{
console.log(result)
const imgurl=result.data.data.url
document.body.style.backgroundImage=`url(${imgurl})`
//2.上传成功了,但是背景图片的需求是一直存在,除非手动改变
//但是一刷新就没有了,所以需要保存在本地浏览器中,刷新也存在
localStorage.setItem('bgcimg',imgurl)
})
//3.网页运行后,获取url网址自动加载
const bgurl=localStorage.getItem('bgcimg')
bgurl && (document.body.style.backgroundImage=`url(${bgurl})`)
})
个人信息设置案例
AJAX原理
XMLHttpRequest
定义:XMLHttpRequest(XHR)对象用于与服务器进行交互。通过xmlhttprequest可以在不刷新页面的情况下请求特定url,获取数据。这允许网页在不影响用户操作的情况下,更新页面的局部内容。xmlhttprequest在AJAX编程中被大量使用
关系:axios内部采用xmlhttprequest与服务器交互
使用xhr
步骤:
- 创建XMLHttpRequest 对象
- 配置请求方法和请求url地址
- 监听loadend事件,接收响应结果
- 发起请求
练习案例 需求:获取并展示所有省份的名字
/**
* 目标:使用XHR携带查询参数,展示某个省下属的城市列表
*/
//1.创建xhr对象
const xhr=new XMLHttpRequest()
//2.配置请求方法和请求参数
xhr.open('GET','http://hmajax.itheima.net/api/city?pname=陕西省')
//3.监听loadend事件,接收响应结果
xhr.addEventListener('loadend',()=>{
console.log(xhr.response)
const data =JSON.parse(xhr.response)
//把结果展示到页面中
console.log(data)
document.querySelector('.my-pname').innerHTML= data.list.join('<br>')
})
//4.发送请求
xhr.send()
xhr查询参数
浏览器提供给服务器的额外信息,让服务器返回浏览器想要的数据
语法:xxxx.com/xxx/xxx?参数名… xhr需要自行将数据拼接到url地址后面
地区查询案例
需求:输入省份和城市名字,查询地区列表
请求地址:请求地址: hmajax.itheima.net/api/area? 参数名 1= 值 1& 参数名 2= 值 2
/**
* 目标: 根据省份和城市名字, 查询对应的地区列表
*/
//1 给查询按钮绑定点击事件,
document.querySelector('.sel-btn').addEventListener('click',()=>{
//2 收集表单中的数据
const pname=document.querySelector('.province').value
const cname=document.querySelector('.city').value
//3 把收集到的信息转化成相应的字符串
//3.1 组织查询参数字符串
const obj={
pname,
cname
}
//创建查询参数对象!!!!
const searchobj=new URLSearchParams(obj)//这个对象的作用就是处理指定的参数对象
//吧查询参数字符串转化成相应的拼接到url后面的字符串格式
const stringobj=searchobj.toString()
//5 后面就是xhl的原生请求发送
const xhr=new XMLHttpRequest()
//4 拼接到url查询地址中
xhr.open('GET',`http://hmajax.itheima.net/api/area?${stringobj}`)
xhr.addEventListener('loadend',()=>{
console.log(xhr.response)
const data=JSON.parse(xhr.response)
console.log(data)
//把其中的list数组转化成相应格式的字符串
const mapdata= data.list.map(item=>{
return `<li class="list-group-item">${item}</li>`
})
document.querySelector('.list-group').innerHTML=mapdata
})
xhr.send()
})
数据提交案例
需求:通过xhr提交用户名和密码,完成注册功能
核心:
- 请求头设置 conten-type :application/json
- 请求体携带JSON字符串
/**
* 目标:使用xhr进行数据提交-完成注册功能
*/
document.querySelector('.reg-btn').addEventListener('click', () => {
const xhr=new XMLHttpRequest()
xhr.open('POST','http://hmajax.itheima.net/api/register')
//绑定监听事件,监听服务器响应
xhr.addEventListener('loadend',()=>{
console.log(xhr.response)
})
//不同于axios,XML原生请求数据需要自己设置请求头类型还有请求体数据
//设置请求头类型
xhr.setRequestHeader('Content-Type','application/json')
//设置请求体数据
const data={
username:'xihongshidiyiming',
password:'123456789'
}
//把data转成json字符串才能发送过去
const stringdata=JSON.stringify(data)
xhr.send(stringdata)
})
Promise
定义:promise 对象用于表示一个异步操作的最终完成(或者失败)及其结果值。
语法:
好处:
- 逻辑更清晰
- 了解axios 函数内部运作机制
- 能解决回调函数地狱问题
promise-三种状态
作用:了解promise对象如何关联的处理函数,以及代码执行顺序
概念:一个promise对象,必然处于以下几种状态之一
- 待定(pending):初始状态,既没有被兑现,也没有被拒绝
- 已兑现(fulfilled):意味着操作成功完成
- 已拒绝(rejected):意味着,操作失败
注意:Promise对象一旦被兑现/拒绝就是已经敲定了,状态无法再被改变
使用Promise+XHR获取省份列表
需求:使用promise管理xhr获取省份列表,并展示到页面上
步骤:
- 创建promise对象
- 执行xhr异步代码,获取省份列表
- 关联成功或失败函数,做后续处理
/**
* 目标:使用Promise管理XHR请求省份列表
* 1. 创建Promise对象
* 2. 执行XHR异步代码,获取省份列表
* 3. 关联成功或失败函数,做后续处理
*/
const p=new Promise((resolve,reject)=>{
//执行XHR异步代码,获取省份列表
const xhr=new XMLHttpRequest()
xhr.open('GET','http://hmajax.itheima.net/api/province')
xhr.addEventListener('loadend',()=>{
//xhr如何判断相应成功还是失败的?
//2xx开头的都是成功响应的状态码
if(xhr.status>=200&&xhr.status<300){
//如果成功,则调用resolve函数
resolve(JSON.parse(xhr.response))
}else{
//如果失败则调用reject函数,而函数一般需要创建一个error对象
reject(new Error(xhr.response))
return reject(xhr.response)
}
})
xhr.send()
})
//前面是功能,后面是使用
p.then(result=>{
console.log(result)
}).catch(error=>{
console.log(error)
})
封装简易版axios
myaxios获取省份列表
需求:基于promise+xhr封装myaxios函数,获取省份列表展示 步骤:
- 定义myaxios函数,接收配置对象,返回promise对象
- 发起xhr请求,默认请求方法为get
- 调用成功/失败的处理程序
- 使用myaxios函数,获取省份列表展示
/**
* 目标:封装_简易axios函数_获取省份列表
* 1. 定义myAxios函数,接收配置对象,返回Promise对象
* 2. 发起XHR请求,默认请求方法为GET
* 3. 调用成功/失败的处理程序
* 4. 使用myAxios函数,获取省份列表展示
*/
//1.定义myAxios函数,接受配置对象,返回promise对象
function myAxios(config){
return new Promise((resolve,reject)=>{
//2.发起xhr请求,默认请求方法为get
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程序,获取省份列表展示
myAxios({
url:'http://hmajax.itheima.net/api/province'
}).then(result=>{
console.log(result)
document.querySelector('div').innerHTML=result.list
}).catch(error=>{
console.log(error)
document.querySelector('div').innerHTML=error.message
})
获取地区列表
需求:修改myaxios函数支持传递查询参数,获取“辽宁省”,“大连市”对应地区列表展示 步骤:
- myaxios函数调用后,判断params选项
- 基于URLSearchParams转换查询参数字符串
- 使用自己封装的myaxios函数展示地区列表
/**
* 目标:封装_简易axios函数_获取地区列表
* 1. 判断有params选项,携带查询参数
* 2. 使用URLSearchParams转换,并携带到url上
* 3. 使用myAxios函数,获取地区列表
*/
function myAxios(config){
return new Promise((resolve,reject)=>{
//创建xhr对象
const xhr= new XMLHttpRequest()
if(config.params){
const paramsobj=new URLSearchParams(config.params)
const paramsstring =paramsobj.toString()
config.url+=`?${paramsstring}`
}
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:'西红柿lallalalalal',
cname:'123456789'
}
}).then(result=>{
console.log(result)
document.querySelector('.my-p').innerHTML=result.list
}).catch(error=>{
console.log(error)
document.querySelector('.my-p').innerHTML=error.message
})
注册用户
需求:修改myaxios函数支持传递请求体数据,完成注册用户功能
步骤:
- myaxios函数调用后,判断data选项
- 转换数据类型,在send方法中发送
- 使用自己封装的myaxios函数完成注册用户功能
/**
* 目标:封装_简易axios函数_注册用户
* 1. 判断有data选项,携带请求体
* 2. 转换数据类型,在send中发送
* 3. 使用myAxios函数,完成注册用户
*/
function myAxios(config){
return new Promise((resolve,reject)=>{
//创建xhr请求体
const xhr=new XMLHttpRequest()
//判断是否有params参数
if(config.params){
const paramsobj=new URLSearchParams(config.params)
const paramsstring=JSON.parse(paramsobj)
config.url+=`?${paramsstring}`
}
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){
const stringdata=JSON.stringify(config.data)
xhr.setRequestHeader('Content-Type','application/json')
xhr.send(stringdata)
}else{
xhr.spend()
}
})
}
myAxios({
url:'http://hmajax.itheima.net/api/register',
method:'POST',
data:{
username:'西红柿lalalallalal',
password:'123456789123456'
}
}).then(result=>{
console.log(result)
}).catch(error=>{
console.log(error)
})
案例-天气预报
默认展示背景天气预报
步骤:
- 获取北京市天使数据,展示
- 搜索城市列表,展示
- 点击城市,显示对应天气数据
/**
* 目标1:默认显示-北京市天气
* 1.1 获取北京市天气数据
* 1.2 数据展示到页面
*/
//由于函数多次被调用,封装函数
function getWeather(citycode){
//获取天气数据
myAxios({
url:'http://hmajax.itheima.net/api/weather',
params:{
city:`${citycode}`
}
}).then(result=>{
console.log(result)
const data=result.data
//日期
const dataobj=`<span class="dateShort">${data.dateShort}</span>
<span class="calendar">农历
<span class="dateLunar">${data.dateLunar}</span>
</span>`
document.querySelector('.title').innerHTML=dataobj
//城市名
const cityname=`
<img src="./imgs/定位.png" alt="">
<span class="area">${data.area}</span>`
document.querySelector('.location').innerHTML=cityname
//当天气温
const todaytemputer=` <div class="tem-box">
<span class="temp">
<span class="temperature">${data.temperature}</span>
<span>°</span>
</span>
</div>
<div class="climate-box">
<div class="air">
<span class="psPm25">${data.psPm25}</span>
<span class="psPm25Level">${data.psPm25Level}</span>
</div>
<ul class="weather-list">
<li>
<img src="${data.weatherImg}" class="weatherImg" alt="">
<span class="weather">${data.weather}</span>
</li>
<li class="windDirection">${data.windDirection}</li>
<li class="windPower">${data.windPower}</li>
</ul>
</div>`
document.querySelector('.weather-box').innerHTML=todaytemputer
//当天天气
const todaywhether=` <div class="range-box">
<span>今天:</span>
<span class="range">
<span class="weather">${data.weather}</span>
<span class="temNight">${data.todayWeather.temNight}</span>
<span>-</span>
<span class="temDay">${data.todayWeather.temDay}</span>
<span>℃</span>
</span>
</div>
<ul class="sun-list">
<li>
<span>紫外线</span>
<span class="ultraviolet">${data.todayWeather.ultraviolet}</span>
</li>
<li>
<span>湿度</span>
<span class="humidity">${data.todayWeather.humidity}</span>%
</li>
<li>
<span>日出</span>
<span class="sunriseTime">${data.todayWeather.sunriseTime}</span>
</li>
<li>
<span>日落</span>
<span class="sunsetTime">${data.todayWeather.sunsetTime}</span>
</li>
</ul>`
document.querySelector('.today-weather').innerHTML=todaywhether
//一周天气预报
//因为重复的很多所以可以使用map函数映射
const weekswheather=data.dayForecast
const weekswheatherStr=weekswheather.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=weekswheatherStr
})
}
//一进入网页默认获取北京市天气,所以传入北京市城市编号
getWeather('110100')
搜索城市列表
需求:根据关键字,展示匹配城市列表
步骤:
- 绑定input事件,获取关键字
- 获取展示城市列表数据
/**
* 目标2:搜索城市列表
* 2.1 绑定input事件,获取关键字
* 2.2 获取展示城市列表数据
*/
//绑定input事件
//input事件,时刻监听表单中的数据变化
document.querySelector('.search-city').addEventListener('input',e=>{
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 citylist=result.data
const citylistStr=citylist.map(item=>{
return `<li class="city-item" data-code='${item.code}' >${item.name}</li>`
}).join('')
document.querySelector('.search-list').innerHTML=citylistStr
})
})
展示城市天气
需求: 展示用户搜索查看的城市天气
步骤:
- 检测搜索列表点击事件,获取城市code值
- 服用获取展示城市天气函数
/**
* 目标3:切换城市天气
* 3.1 绑定城市点击事件,获取城市code值
* 3.2 调用获取并展示天气的函数
*/
document.querySelector('.search').addEventListener('click',e=>{
if(e.target.classList.contains('city-item')){
// console.log('西红柿')
const citycodee=e.target.dataset.code
console.log(citycodee)
//调用天气函数
getWeather(citycodee)
}
})
AJAX进阶
同步代码和异步代码
同步代码:逐行执行,需原地等待结果后,才继续向下执行
异步代码:调用后耗时,不阻塞代码继续执行(不必原地等待),在将来完成后触发一个回调函数
js中有哪些异步代码?
- seTtimeout/setInterval
- 事件
- Ajax
异步代码如何接收结果? 依靠回调函数来接受
回调函数地狱和promise链式调用
回调函数地狱
需求:展示默认第一个省,第一个城市,第一个地区在下拉菜单中
概念:在回调函数中嵌套回调函数,一直嵌套下去就形成了回调函数地狱
缺点:可读性差,异常无法捕获,耦合性严重,牵一发动全身
promise-链式调用
概念:依靠then()方法会返回一个新生成的 promise对象特性,继续串联下一环任务,直到结束
细节:then()回调函数中的返回值,会影响新生成的promise对象最终状态和结果
好处:通过链式调用,解决回调函数嵌套问题
promise链式应用
目标:使用promise链式调用,解决回调函数地域问题
做法:每个promise对象中管理一个异步任务,用then返回promise对象,串联起来
async和await使用
概念:在 async 函数内,使用 await 关键字取代 then 函数, 等待 获取 Promise 对象 成功状态的结果值
async function getData(){
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}})
console.log(aobj)
const aname=aobj.data.list[0]
document.querySelector('.province').innerHTML=pname
document.querySelector('.city').innerHTML=cname
document.querySelector('.area').innerHTML=aname
async函数和await 捕获错误
使用: try...catch ,该语句标记要尝试的语句块,并指定一个出现异常时抛出的响应。
语法:
事件循环-EventLoop
好处:掌握JavaScript是如何安排和运行代码的
概念:JavaScript有一个基于事件循环的并发模型,事件循环负责执行代码,收集和处理时间以及执行队列中的子任务。这个模型与其它语言中的模型截然不同,比如c和java
原因:JavaScript是单线程(某一时刻只能执行一行代码),为了让耗时代码不阻塞其他代码运行,设计了事件循环模型
事件循环-执行过程
定义:执行的起码和收集异步任务的模型,在调用栈空闲,反复调用任务队列里回调函数的执行机制,就叫做事件循环
执行过程:
- 执行同步代码,遇到异步代码交给宿主浏览器环境执行
- 异步有了结果后,把回调函数放入任务队列里面排队
- 当调用栈空闲后,反复调用任务队列里的回调函数
宏任务与微任务
Es6之后引入了Promise对象,让js引擎也可以发起异步任务
异步任务分为:
- 宏任务:由浏览器环境执行的异步代码
- 微任务:由js引擎环境执行的异步代码
promise.all静态方法
概念:合并多个Promise对象,等待多有同时成功完成(或某一个失败),做后续逻辑
语法:
案例
需求:同时请求北京 上海 广州 深圳 的天气并在网页上尽可能同时显示
/**
* 目标:掌握Promise的all方法作用,和使用场景
* 业务:当我需要同一时间显示多个请求的结果时,就要把多请求合并
* 例如:默认显示"北京", "上海", "广州", "深圳"的天气在首页查看
* code:
* 北京-110100
* 上海-310100
* 广州-440100
* 深圳-440300
*/
//1.请求城市天气,得到promise对象
const bjpromise=axios({url:'http://hmajax.itheima.net/api/weather',params:{city:'110100'}})
const shpromise=axios({url:'http://hmajax.itheima.net/api/weather',params:{city:'310110'}})
const gzpromise=axios({url:'http://hmajax.itheima.net/api/weather',params:{city:'440100'}})
const szpromise=axios({url:'http://hmajax.itheima.net/api/weather',params:{city:'440300'}})
//使用promise.all,合并多个promise对象
const p = Promise.all([bjpromise,shpromise,gzpromise,szpromise])
p.then(result=>{
//结果数组和合并时顺序是一致的
console.log(result)
}).catch(error=>{
console.dir(error)
})
案例-商品分类
需求:尽可能同时展示所有商品分类到页面上
步骤:
- 获取所有的一级分类数据
- 遍历id,创建获取二级分类请求
- 合并所有二级分类promise对象‘
- 等带同时成功,开始渲染页面
/**
* 目标:把所有商品分类“同时”渲染到页面上
* 1. 获取所有一级分类数据
* 2. 遍历id,创建获取二级分类请求
* 3. 合并所有二级分类Promise对象
* 4. 等待同时成功后,渲染页面
*/
//1.获取所有一级分类数据
axios({
url:'http://hmajax.itheima.net/api/category/top'
}).then(result=>{
console.log(result)
const data=result.data.data
//遍历获取二级对象
const promiselist= data.map(item=>{
return axios({
url:'http://hmajax.itheima.net/api/category/sub',
params:{
id:item.id,
}
})
})
//合并所有二级分类promise对象
console.log(promiselist)
// Promise.all()
const p=Promise.all(promiselist)
p.then(result=>{
const allist=result.map(item=>{
const dataobj=item.data.data
return ` <h3>${dataobj.name}</h3>
<ul>
${
dataobj.children.map(item=>{
return `
<li>
<a href="javascript:;">
<img src='${item.picture}'>
<p>${item.name}</p>
</a>
</li>`
}).join('')
}
</ul>`
}).join('')
//等同时成功后,渲染页面
document.querySelector('.item').innerHTML=allist
})
})