AJAX-笔记(持续更新中)

文章目录

    • 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-笔记(持续更新中)_第1张图片
概念:AJAX是浏览器与服务器进行数据通信的技术
作用:浏览器和服务器之间通信,动态数据交互。
怎么使用AJAX?

1.先使用axios库,与服务器进行数据通信(使用场景:VUE,reat项目都会用到axios)
2.再学习XMLHttpREquest对象的使用,理解AJAX底层原理

axios使用:

  • 1.引入axios库:
  • 2.使用axios函数
    √ 传入配置对象
    √ 再用.then回调函数接收结果,并做后续的处理
axios({
	url:'目标资源地址'
}).then(result => {
//对服务器进行后续的处理
})

2. 认识URL

URL:统一资源定位符,网址,用于访问服务器资源
解释url每个部分的作用:

http://hmajax.itheima.net /api/login
协议域名 资源路径

AJAX-笔记(持续更新中)_第2张图片

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的网络面板查看请求报文

AJAX-笔记(持续更新中)_第3张图片
在这里插入图片描述

AJAX-笔记(持续更新中)_第4张图片

响应报文的组成:

  • 1.响应行:协议、Http响应状态码、状态信息
  • 2.响应头:键值对的格式携带的附加信息,比如Content-Type
  • 3.空行:分隔响应头,空行之后的是返回给浏览器的资源
  • 4.响应体:返回的资源
    AJAX-笔记(持续更新中)_第5张图片
    AJAX-笔记(持续更新中)_第6张图片

Http响应状态码:用来表明请求是否成功完成。

状态码 说明
1xx 信息
2xx 成功
3xx 重定向消息
4xx 客户端错误
5xx 服务端错误

6.接口文档

接口文档:由后端提供的描述接口的文章
AJAX-笔记(持续更新中)_第7张图片

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>
  
  <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>
  
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/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>

AJAX-笔记(持续更新中)_第8张图片

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>
  
  <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
head>

<body>
  
  <button type="button" class="btn btn-primary" data-bs-target = '.my-box' data-bs-toggle="modal">
    显示弹框
  button>
  
  <div class="modal my-box" tabindex="-1">
    <div class="modal-dialog">
      
      <div class="modal-content">
        <div class="modal-header">
          <h5 class="modal-title">Modal titleh5>
          <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">Closebutton>
          <button type="button" class="btn btn-primary">Save changesbutton>
        div>
      div>
    div>
  div>

  
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/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>
  
  <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
head>

<body>
  
  <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>

  
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/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 `
            
          ${index+1}
          ${item.bookname}
          ${item.author}
          ${item.publisher}
          ${item.id}>
            删除
            编辑
          
          
            `    
        }).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,隐藏起来:无需用户修改
// 
   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() 发送请求

AJAX-笔记(持续更新中)_第9张图片
AJAX-笔记(持续更新中)_第10张图片
AJAX-笔记(持续更新中)_第11张图片
AJAX-笔记(持续更新中)_第12张图片

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>

AJAX-笔记(持续更新中)_第13张图片

 目标:使用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('
'
) }).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('
'
) }).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('
'
) }).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('
')
}).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 = `   ${wObj.date}
    农历 
      ${wObj.dateLunar}`
      document.querySelector('.title').innerHTML=dateStr
    //   城市名字
    document.querySelector('.area').innerHTML = wObj.area
    // 当天天气
    const nowWStr = ` 
     
${wObj.temperature} °
${wObj.psPm25} ${wObj.psPm25Level}
  • ${wObj.weatherImg} class="weatherImg" alt=""> ${wObj.weather}
  • ${wObj.windDirection}
  • ${wObj.windPower}
`
document.querySelector('.weather-box').innerHTML = nowWStr // 当前天气 const twObj = wObj.todayWeather console.log(twObj); const todayWStr = `
今天: ${twObj.weather} ${twObj.temNight} - ${twObj.temDay}
  • 紫外线 ${twObj.ultraviolet}
  • 湿度 ${twObj.humidity}%
  • 日出 ${twObj.sunriseTime}
  • 日落 ${twObj.sunsetTime}
`
document.querySelector('.today-weather').innerHTML = todayWStr // 7天天气预报 const dayForecast = wObj.dayForecast const dayForecastStr = dayForecast.map(item =>{ return `
  • ${item.dateFormat} ${item.date}
    ${item.weatherImg}" alt="" class="weatherImg"> ${item.weather}
    ${item.temNight}- ${item.temDay}
    ${item.windDirection} <${item.windPower}
  • `
    }).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 `
  • ${item.code}>${item.name}
  • `
    }).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>
    

    AJAX-笔记(持续更新中)_第14张图片
    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>
    

    AJAX-笔记(持续更新中)_第15张图片

    AJAX-笔记(持续更新中)_第16张图片

    事件循环(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对象,等待所有提示成功完成(或某一个失败),做后续逻辑

    AJAX-笔记(持续更新中)_第17张图片

     <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 `
  • ${area}-----${weather}
  • `
    }).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`

    一级分类名字:${obj.name}

    `
    }).join('') document.querySelector('.sub-list').innerHTML = htmlStr }) }) </script>

    AJAX-笔记(持续更新中)_第18张图片

    学习反馈

     目标1:完成省市区下拉列表切换
      1.1 设置省份下拉菜单数据
      1.2 切换省份,设置城市下拉菜单数据,清空地区下拉菜单
      1.3 切换城市,设置地区下拉菜单数据
    

    AJAX-笔记(持续更新中)_第19张图片

    AJAX-笔记(持续更新中)_第20张图片
    AJAX-笔记(持续更新中)_第21张图片

      目标2:收集数据保存
      2.1 监听提交的点击事件
      2.2 依靠插件收集表单数据
      2.3 基于axios提交保存
    

    AJAX-笔记(持续更新中)_第22张图片

    /**
     * 目标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 `  ">${item}`
        }).join('')
        document.querySelector('.province').innerHTML=``+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 `  ">${item}`
            }).join('')
              // 把默认初始选项+下属城市数据插入select中   
            document.querySelector('.city').innerHTML=``+optionStr 
              //    清空地区数据
            document.querySelector('.area').innerHTML= ``
        })
    })
    
    // 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 `  ">${item}`
            }).join('')
                
            document.querySelector('.area').innerHTML=``+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的大概知识我都清楚了,也在学习过程中,我得到了一些学习经验,比如,看完一天的视频,要回去复习,不然容易忘记,边看视频边敲代码边理解或者看完一天视频再敲代码,可能带着回忆的状态,这样子可以检验自己是否真的懂(可能这个方法会好一点,因人而异),还有就是代码也不是敲一遍就行的,最多敲三遍,最后一遍一定是自己明白了,在不借助外力的情况下,自己敲(虽然但是,我还没达到那种境界,在努力中)。
    最后送大家一句话:博学之,审问之,慎思之,明辨之,笃行之。

    你可能感兴趣的:(ajax,笔记,okhttp)