AJAX 基础

218 阅读9分钟

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 函数

image.png

认识 URL

知道url作用和组成,方便和后端人员沟通

什么是url

定义:统一资源定位符,或统一资源定位器,定位地址,url地址,简称网址,访问网络上的资源

url的组成

image.png

http协议:超文本传输协议,规定浏览器和服务器之间传输数据的格式

域名:标记服务器在互联网中方位

资源路径:标记资源在服务器下的具体位置

image.png

获取新闻列表

需求:使用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的?后面,参数名=值

地区查询案例

需求: 根据输入的省份名字和城市名字,查询地区并且渲染列表

image.png 过程:确定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
    }) 

    }) 

常用请求方法和数据提交

请求方法:对服务器资源,要执行的操作

image.png

####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方法,传入回调函数并定义形参

image.png

HTTP协议-报文

http协议:规定了浏览器发送及服务器返回内容的格式

请求报文:浏览器按照http协议要求的格式,发送给服务器的内容

请求报文的格式

请求报文的组成部分有:

  1. 请求行:请求方法,url,协议
  2. 请求头:以键值对的格式携带的附加信息,比如:contene-type
  3. 空行:分隔请求头,空行之后是发送给服务器的资源
  4. 请求体:发送给服务器的资源

image.png

image.png

响应报文

服务器按照http协议要求的格式,返回给浏览器的内容

  1. 响应行(状态行):协议,http响应状态码,状态信息
  2. 其余同请求报文
响应状态码

image.png

接口文档

接口文档: 描述接口的文章(后端工程师)

接口:使用ajax和服务器进行通讯时,使用的url,请求方法,以及参数

黑马ajax接口文档:传送门

案例-用户登录

  1. 点击登陆时,判断用户名和密码长度
  2. 提交数据和服务器通信
  3. 提示信息
<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 弹框

功能:不离开当前页面,显示单独内容,供用户操作

步骤:

  1. 引入bootstrap.css和bootstrap.js
  2. 准备弹框标签,确认结构
  3. 通过自定义属性,控制弹框的显示和隐藏(单纯显示隐藏)

image.png

 <!-- 
    目标:使用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>
  1. 也可以通过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()
        })
        
}) 

图片上传

  1. 获取图片文件对象
  2. 使用formdata携带图片文件

image.png 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>

网站换肤案例

网站更换背景

  1. 选择图片上传,设置body背景
  2. 上传成功时,保存url网址
  3. 网页运行后,获取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

步骤:

  1. 创建XMLHttpRequest 对象
  2. 配置请求方法和请求url地址
  3. 监听loadend事件,接收响应结果
  4. 发起请求

练习案例 需求:获取并展示所有省份的名字

   /**
     * 目标:使用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()

image.png

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提交用户名和密码,完成注册功能

核心:

  1. 请求头设置 conten-type :application/json
  2. 请求体携带JSON字符串

image.png

 /**
     * 目标:使用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 对象用于表示一个异步操作的最终完成(或者失败)及其结果值。

语法:

image.png

好处:

  1. 逻辑更清晰
  2. 了解axios 函数内部运作机制
  3. 能解决回调函数地狱问题
promise-三种状态

作用:了解promise对象如何关联的处理函数,以及代码执行顺序

概念:一个promise对象,必然处于以下几种状态之一

  • 待定(pending):初始状态,既没有被兑现,也没有被拒绝
  • 已兑现(fulfilled):意味着操作成功完成
  • 已拒绝(rejected):意味着,操作失败

注意:Promise对象一旦被兑现/拒绝就是已经敲定了,状态无法再被改变

image.png

使用Promise+XHR获取省份列表

需求:使用promise管理xhr获取省份列表,并展示到页面上

步骤:

  1. 创建promise对象
  2. 执行xhr异步代码,获取省份列表
  3. 关联成功或失败函数,做后续处理
    /**
     * 目标:使用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函数,获取省份列表展示 步骤:

  1. 定义myaxios函数,接收配置对象,返回promise对象
  2. 发起xhr请求,默认请求方法为get
  3. 调用成功/失败的处理程序
  4. 使用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函数支持传递查询参数,获取“辽宁省”,“大连市”对应地区列表展示 步骤:

  1. myaxios函数调用后,判断params选项
  2. 基于URLSearchParams转换查询参数字符串
  3. 使用自己封装的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函数支持传递请求体数据,完成注册用户功能

步骤:

  1. myaxios函数调用后,判断data选项
  2. 转换数据类型,在send方法中发送
  3. 使用自己封装的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. 获取北京市天使数据,展示
  2. 搜索城市列表,展示
  3. 点击城市,显示对应天气数据
/**
 * 目标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">农历&nbsp;
        <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')
搜索城市列表

需求:根据关键字,展示匹配城市列表

步骤:

  1. 绑定input事件,获取关键字
  2. 获取展示城市列表数据
/**
 * 目标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

    })
})
展示城市天气

需求: 展示用户搜索查看的城市天气

步骤:

  1. 检测搜索列表点击事件,获取城市code值
  2. 服用获取展示城市天气函数
/**
 * 目标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中有哪些异步代码?

  1. seTtimeout/setInterval
  2. 事件
  3. Ajax

异步代码如何接收结果? 依靠回调函数来接受

回调函数地狱和promise链式调用

回调函数地狱

需求:展示默认第一个省,第一个城市,第一个地区在下拉菜单中

概念:在回调函数中嵌套回调函数,一直嵌套下去就形成了回调函数地狱

缺点:可读性差,异常无法捕获,耦合性严重,牵一发动全身

image.png

promise-链式调用

概念:依靠then()方法会返回一个新生成的 promise对象特性,继续串联下一环任务,直到结束

细节:then()回调函数中的返回值,会影响新生成的promise对象最终状态和结果

好处:通过链式调用,解决回调函数嵌套问题

image.png

promise链式应用

目标:使用promise链式调用,解决回调函数地域问题

做法:每个promise对象中管理一个异步任务,用then返回promise对象,串联起来

image.png



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 ,该语句标记要尝试的语句块,并指定一个出现异常时抛出的响应。

语法:

image.png

事件循环-EventLoop

好处:掌握JavaScript是如何安排和运行代码的

概念:JavaScript有一个基于事件循环的并发模型,事件循环负责执行代码,收集和处理时间以及执行队列中的子任务。这个模型与其它语言中的模型截然不同,比如c和java

原因:JavaScript是单线程(某一时刻只能执行一行代码),为了让耗时代码不阻塞其他代码运行,设计了事件循环模型

事件循环-执行过程

定义:执行的起码和收集异步任务的模型,在调用栈空闲,反复调用任务队列里回调函数的执行机制,就叫做事件循环

执行过程:

  • 执行同步代码,遇到异步代码交给宿主浏览器环境执行
  • 异步有了结果后,把回调函数放入任务队列里面排队
  • 当调用栈空闲后,反复调用任务队列里的回调函数

image.png

宏任务与微任务

Es6之后引入了Promise对象,让js引擎也可以发起异步任务

异步任务分为:

  • 宏任务:由浏览器环境执行的异步代码
  • 微任务:由js引擎环境执行的异步代码

image.png

image.png

promise.all静态方法

概念:合并多个Promise对象,等待多有同时成功完成(或某一个失败),做后续逻辑

image.png

语法:

image.png

案例

需求:同时请求北京 上海 广州 深圳 的天气并在网页上尽可能同时显示

 /**
     * 目标:掌握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)
     })

案例-商品分类

需求:尽可能同时展示所有商品分类到页面上

步骤:

  1. 获取所有的一级分类数据
  2. 遍历id,创建获取二级分类请求
  3. 合并所有二级分类promise对象‘
  4. 等带同时成功,开始渲染页面
/**
     * 目标:把所有商品分类“同时”渲染到页面上
     *  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

        })

})

案例-学习反馈