小白ajax入门学习笔记

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

一、AJAX 入门——让数据活起来

1.1AJAX 概念和 axios 使用

1.1.1什么是 AJAX [ˈeɪdʒæks] ?

定义:https://developer.mozilla.org/zh-CN/docs/Web/Guide/AJAX
在这里插入图片描述
概念:AJAX 是浏览器与服务器进行数据通信的技术
在这里插入图片描述

1.1.2怎么用 AJAX ?

  1. 先使用 axios [æk‘sioʊs] 库axios库:https://axios-http.com/zh/,与服务器进行数据通信
    ⚫ 基于 XMLHttpRequest 封装、代码简单、月下载量在 14 亿次
    Vue、React 项目中都会用到 axios
  2. 再学习 XMLHttpRequest 对象的使用,了解 AJAX 底层原理

1.1.3axios 使用

语法:

  1. 引入 axios.js:https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js
  2. 使用 axios 函数
    ✓ 传入配置对象
    ✓ 再用 .then 回调函数接收结果,并做后续处理

1.1.4运用axios

需求:请求目标资源地址,拿到省份列表数据,显示到页面
目标资源地址:http://hmajax.itheima.net/api/province
在这里插入图片描述
获取的结果存在data里面的list里面
在这里插入图片描述
取data的list里面的值console.log(result.data.list)得到一个数组包含省份
在这里插入图片描述

<body>
    <p class="province"></p>
    <!-- axios库地址:https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js
    省份数据地址:http://hmajax.itheima.net/api/province
    需求:通过使用axios库,获取省份列表数据,展示到页面上
    1.引入axios
    -->
    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
    <!-- 2.使用axios函数 -->
    <script>
        axios({
            //url告诉axios去哪个服务器地址获取数据
            url:'http://hmajax.itheima.net/api/province'
        }).then(result => {
            
            console.log(result); //返回一个对象
            console.log(result.data.list); //返回包含省份的数组
            console.log(result.data.list.join('<br>')); //数组转字符串
            //把省份列表渲染到页面上
            document.querySelector('.province').innerHTML = result.data.list.join('<br>')
        })
    </script>
</body>

在这里插入图片描述

在这里插入图片描述

1.1.5 总结

  1. AJAX 有什么用?
    浏览器和服务器之间通信,动态数据交互
  2. AJAX 如何学:
    ➢ 先掌握 axios 使用
    ➢ 再了解 XMLHttpRequest 原理
  3. 这一节 axios 体验步骤?
    ➢ 引入 axios 库
    ➢ 使用 axios 语法
    在这里插入图片描述

1.2认识 URL

1.2.1认识 URL

在这里插入图片描述

1.2.2什么是 URL?

在这里插入图片描述
在这里插入图片描述
概念:URL 就是统一资源定位符,简称网址,用于访问网络上的资源

1.2.3URL 的组成

在这里插入图片描述

1.2.3.1协议

http 协议超文本传输协议,规定浏览器和服务器之间传输数据的格式
在这里插入图片描述

1.2.3.2域名

域名标记服务器在互联网中方位
在这里插入图片描述

1.2.3.3资源路径

**资源路径:**标记资源在服务器下的具体位置
在这里插入图片描述

1.2.4案例:获取 - 新闻列表

需求:使用 axios 从服务器拿到新闻列表数据
目标资源地址:http://hmajax.itheima.net/api/news
在这里插入图片描述

<body>
    <!-- 
    新闻数据地址: 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);
            console.log(result.data.data);
        })
    </script>
</body>

在这里插入图片描述

1.2.5总结

  1. URL 是什么:
    ➢ 统一资源定位符,网址,用于访问服务器上资源
  2. 请解释这个 URL,每个部分作用?
    ➢ http://hmajax.itheima.net/api/news
    ➢ 协议://域名/资源路径

1.3URL 查询参数

1.3.1

定义:浏览器提供给服务器的额外信息,让服务器返回浏览器想要的数据
语法:http://xxxx.com/xxx/xxx?参数名1=值1&参数名2=值2
?,&
在这里插入图片描述

1.3.2axios-查询参数

**语法:**使用 axios 提供的 params 选项
注意:axios 在运行时把参数名和值,会拼接到 url?参数名=值
在这里插入图片描述

<body>
    <p class="city"></p>
    <!-- axios库地址:https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js
    城市数据地址:http://hmajax.itheima.net/api/city
    需求:通过使用axios库,获取省份列表数据,展示到页面上
    1.引入axios
    -->
    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
    <script>
        axios({
            url:'http://hmajax.itheima.net/api/city',
            params:{
                pname:'广东省'
            }
        }).then(result =>{
            console.log(result); //返回一个对象
            console.log(result.data.list); //返回包含城市的数组
            console.log(result.data.list.join('<br>')); //数组转字符串
            //把城市列表渲染到页面上
            document.querySelector('.city').innerHTML = result.data.list.join('<br>')
        })
    </script>
</body>

在这里插入图片描述

1.3.3总结

  1. URL 查询参数有什么作用?
    ➢ 浏览器提供给服务器额外信息,获取对应的数据
  2. axios 要如何携带查询参数?
    ➢ 使用 params 选项,携带参数名和值
    在这里插入图片描述

1.3.4案例 地区查询

需求:根据输入的省份名字和城市名字,查询地区并渲染列表
在这里插入图片描述
步骤 地区查询
首先:确定 URL 网址参数说明
• 查询某个内某个城市的所有地区: http://hmajax.itheima.net/api/area
• 参数名:
pname:省份名字或直辖市名字,比如北京、福建省、辽宁省…
cname:城市名字,比如北京市、厦门市、大连市…
完整:http://hmajax.itheima.net/api/area?pname=北京&cname=北京市

<body>
  <div class="container">
    <form id="editForm" class="row">
      <!-- 输入省份名字 -->
      <div class="mb-3 col">
        <label class="form-label">省份名字</label>
        <input type="text" value="北京" name="province" class="form-control province" placeholder="请输入省份名称" />
      </div>
      <!-- 输入城市名字 -->
      <div class="mb-3 col">
        <label class="form-label">城市名字</label>
        <input type="text" value="北京市" name="city" class="form-control city" placeholder="请输入城市名称" />
      </div>
    </form>
    <button type="button" class="btn btn-primary sel-btn">查询</button>
    <br><br>
    <p>地区列表: </p>
    <ul class="list-group">
      <!-- 示例地区 -->
      <li class="list-group-item">东城区</li>
    </ul>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
  <script>
    /*
      获取地区列表: http://hmajax.itheima.net/api/area
      查询参数:
        pname: 省份或直辖市名字
        cname: 城市名字
    */
   //需求:根据省份和城市的名字,查询地区列表
   //1.查询按钮--绑定点击事件
   document.querySelector('.sel-btn').addEventListener('click',()=>{
    console.log('点击');
    //获取用户输入省份和城市名字
    let provinceName = document.querySelector('.province').value
    let cityName = document.querySelector('.city').value
    console.log(provinceName,cityName);

    axios({
      url:'http://hmajax.itheima.net/api/area',
      params:{
        pname:provinceName,
        cname:cityName
      }
    }).then(result =>{
      //对获取的数据进行渲染处理
      console.log(result.data.list);//得到一个数组
      //数组映射
      //map()函数遍历数组,()里面添加对每个元素的操作。最后返回一个数组
      let cityLi = result.data.list.map(areaName => `<li class="list-group-item">${areaName}</li>`)
  
      console.log(cityLi); //map返回的是一个字符串
      //字符串拼接,加入ul的内容里面
      document.querySelector('.list-group').innerHTML = cityLi.join('')
    })
   })
  </script>
</body>

在这里插入图片描述
在这里插入图片描述

1.4常用请求方法和数据提交

1.4.1常用请求方法

请求方法:对服务器资源,要执行的操作
在这里插入图片描述

1.4.2数据提交

场景:当数据需要在服务器上保存
在这里插入图片描述

1.4.3axios 请求配置

url:请求的 URL 网址
method:请求的方法,GET可以省略(不区分大小写)
在这里插入图片描述
data:提交数据
在这里插入图片描述

1.4.4数据提交-注册账号

需求:通过 axios 提交用户名和密码,完成注册功能
注册用户 URL 地址:http://hmajax.itheima.net/api/register
请求方法:POST
参数名:
username 用户名(中英文和数字组成,最少 8 位)
password 密码(最少 6 位)
在这里插入图片描述

在这里插入图片描述

<body>
  <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:'shaojunhao02',
          password:'12345678'
        }
      }).then(result =>{
        console.log(result.data.message);
      })
    })
  </script>
</body>

在这里插入图片描述
重复注册报错提示账号被占用
在这里插入图片描述

1.4.5总结

  1. 请求方法表明对服务器资源的操作,最为常用的2个是?
    POST 提交数据,GET 查询数据
  2. axios 的核心配置?
    ➢ url:请求 URL 网址
    ➢ method:请求方法,GET 可以省略(不区分大小写)
    ➢ params:查询参数
    ➢ data:提交数据
    在这里插入图片描述

1.4.6axios 错误处理

在这里插入图片描述
语法:then 方法的后面,通过点语法调用 catch 方法,传入回调函数并定义形参
在这里插入图片描述
处理:注册案例,重复注册时通过弹框提示用户错误原因
需求:捕获错误信息并弹窗显示
在这里插入图片描述

<body>
  <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: 'shaojunhao02',
        password: '12345678'
      }
    }).then(result => {
      //console.log(result)//
    }).catch(error =>{
      //在then方法的后面,通过点语法调用catch方法,传入回调函数并定义形参
      console.log(error);
      alert(error.response.data.message) //账号被占用
    })
   })
  </script>
</body>

1.5HTTP协议-报文(浏览器是怎么把内容发送给服务器的)

1.5.1HTTP 协议-请求报文

HTTP 协议:规定了浏览器发送及服务器返回内容的格式
请求报文:浏览器按照 HTTP 协议要求的格式,发送给服务器的内容
在这里插入图片描述

1.5.2请求报文的格式

请求报文的组成部分有:

  1. 请求行:请求方法,URL,协议
  2. 请求头:以键值对的格式携带的附加信息,比如:Content-Type
  3. 空行:分隔请求头,空行之后的是发送给服务器的资源
  4. 请求体:发送的资源
    在这里插入图片描述
    请求报文的组成部分有:
  5. 请求行:请求方法,URL,协议
  6. 请求头:以键值对的格式携带的附加信息,比如:Content-Type
  7. 空行:分隔请求头,空行之后的是发送给服务器的资源
  8. 请求体:发送的资源
    在这里插入图片描述
    在这里插入图片描述

1.5.3浏览器里查看报文的格式

在这里插入图片描述
在这里插入图片描述
开发者工具–>网络面板–>Fetch/XHR–>左边找到想看的某一次请求–>右边的详细面板–>标头headers看–>载荷Rayload看数据
在这里插入图片描述

1.5.4总结

  1. 浏览器发送给服务器的内容叫做请求报文
  2. 请求报文的组成:
    在这里插入图片描述
  3. 通过 Chrome 的网络面板查看请求报文
    在这里插入图片描述

1.5.5请求报文-错误排查

需求:通过请求报文排查错误原因,并修复
输入正确的用户名和密码无法登录
• 用户名:itheima007
• 密码:7654321
在这里插入图片描述

1.5.6HTTP 协议-响应报文(对应请求报文)

HTTP 协议:规定了浏览器发送及服务器返回内容的格式
响应报文:服务器按照 HTTP 协议要求的格式,返回给浏览器的内容

  1. 响应行(状态行):协议、HTTP 响应状态码、状态信息
  2. **响应头:**以键值对的格式携带的附加信息,比如:Content-Type
  3. 空行:分隔响应头,空行之后的是服务器返回的资源
  4. 响应体:返回的资源
    在这里插入图片描述
    在这里插入图片描述

1.5.7HTTP 响应状态码

HTTP 响应状态码:用来表明请求是否成功完成
比如:404(服务器找不到资源)
在这里插入图片描述
浏览器中查看
在这里插入图片描述

1.5.8总结

  1. 响应报文的组成:
    响应行(状态行):协议、HTTP 响应状态码、状态信息
    响应头:以键值对的格式携带的附加信息,比如 Content-Type
    空行:分隔响应头,空行之后的是返回给浏览器的资源
    响应体:返回的资源

    在这里插入图片描述
    2. HTTP 响应状态码用来表明请求是否成功完成
    在这里插入图片描述

1.6接口文档

接口文档:描述接口的文章
**接口:**使用 AJAX 和服务器通讯时,使用的 URL,请求方法,以及参数
传送门:AJAX 阶段接口文档https://apifox.com/apidoc/shared-1b0dd84f-faa8-435d-b355-5a8a329e34a8
在这里插入图片描述

总结:

  1. 接口文档:
    后端提供的描述接口的文章
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

1.7案例 - 用户登录

  1. 点击登录时,判断用户名和密码长度
  2. 提交数据和服务器通信
  3. 提示信息

1.7.1案例-用户登录

在这里插入图片描述
在这里插入图片描述

<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: 封装提示框函数,重复调用,满足需求
    //2.1获取提示框
    const alert = document.querySelector('.alert')
    //2.2封装函数:
    function alertFun(msg,isSuccess){
      //1.显示提示框
      alert.classList.add('show')
      //2.设置不同提示文字
      alert.innerHTML = msg
      //3.背景颜色
      // if(isSuccess){
      // //样式中已经默认添加成功 先删除再添加
      //   alert.classList.remove('alert-danger')
      //   alert.classList.add('alert-success')
      // }else{
      //   alert.classList.remove('alert-success')
      //   alert.classList.add('alert-danger')
      // }
      //三元表达式 判断背景颜色用哪个
      const bgcstyle = isSuccess ? 'alert-success':'alert-danger'
      alert.classList.add(bgcstyle)
      //问题:多次添加后会两个都添加导致类名冲突,解决,可以在提示框消失后移除类名
      
      //4.过两秒后提示框消失
      setTimeout(()=>{
        alert.classList.remove('show')
        alert.classList.remove(bgcstyle)
      },2000)
    }

    //1.1登录绑定点击事件
    document.querySelector('.btn-login').addEventListener('click',()=>{
      //获取用户名和密码
      const username = document.querySelector('.username').value
      const password = document.querySelector('.password').value

      //判断长度
      if(username.length<8){
        alertFun('用户名必须大于等于8位',false)
        console.log('用户名必须大于等于8位');
        //如果不符合,直接提前结束
        return
      }
      if(password.length<6){
        alertFun('密码必须大于等于6位',false)
        console.log('密码必须大于等于6位');
        //如果不符合,直接提前结束
        return
      }
      //axios提交用户名和密码
      axios({
        url:'http://hmajax.itheima.net/api/login',
        method:'post',
        data:{
          username,
          password
        }
      }).then(result => {
        alertFun(result.data.message,true)
        console.log(result.data.message);
      }).catch(error =>{ //返回错误信息
        alertFun(error.response.data.message,false)
        console.log(error.response.data.message);
      })
      
    })
  </script>
</body>

1.8form-serialize 插件

在这里插入图片描述
在这里插入图片描述
第一个参数传:表单对象
//表单元素设置的name属性的值会作为serialize返回的对象的属性名
//建议name属性的值,最好和接口文档参数名一致
第二个参数传:配置对象一般只需{hash:true,empty:true}
//hash 设置获取数据结构 true:获取JS对象结构 false:查询字符串
//empty 设置是否获取空置

返回一个对象:
在这里插入图片描述
需要用哪个再取哪个对象的值

<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 src="./lib/form-serialize.js"></script>
  <script>
    document.querySelector('.btn').addEventListener('click', () => {
      //2.使用serialize函数,快速收集表单数据
      //第1个参数传:表单对象
      //表单元素设置的name属性的值会作为serialize返回的对象的属性名
      //建议name属性的值,最好和接口文档参数名一致
      //第2个参数传:配置对象 ,一般只需{hash:true,empty:true}
      //hash 设置获取数据结构  true:获取JS对象结构  false:查询字符串
        
      //empty 设置是否获取空置
      const form= document.querySelector('.example-form')
      const data = serialize(form,{hash:true,empty:true})
      console.log(data);//{uname: '11', pwd: '22'}
      //上面的uname和pwd是根据表单元素的name属性值来获取的
    })
  </script>
</body>

1.8.1案例-用户登录

使用 form-serialize 插件,收集用户名和密码
在这里插入图片描述

<body>
  <div class="container">
    <h3>欢迎-登录</h3>
    <!-- 登录结果-提示框 -->
    <div class="alert alert-success" role="alert">
      提示消息
    </div>
    <!-- 表单 -->
    <div class="form_wrap">
      <form class="login-form">
        <div class="mb-3">
          <label for="username" class="form-label">账号名</label>
          <input type="text" class="form-control username" name="username">
        </div>
        <div class="mb-3">
          <label for="password" class="form-label">密码</label>
          <input type="password" class="form-control password" name="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 src="./lib/form-serialize.js"></script>
  <script>
    // 需求1:点击登录时,用户名和密码长度判断,并提交数据和服务器通信
    // 需求2: 封装提示框函数,重复调用,满足需求
    //2.1获取提示框
    const alert = document.querySelector('.alert')
    //2.2封装函数:
    function alertFun(msg,isSuccess){
      //1.显示提示框
      alert.classList.add('show')
      //2.设置不同提示文字
      alert.innerHTML = msg
      //3.背景颜色
      // if(isSuccess){
      // //样式中已经默认添加成功 先删除再添加
      //   alert.classList.remove('alert-danger')
      //   alert.classList.add('alert-success')
      // }else{
      //   alert.classList.remove('alert-success')
      //   alert.classList.add('alert-danger')
      // }
      //三元表达式 判断背景颜色用哪个
      const bgcstyle = isSuccess ? 'alert-success':'alert-danger'
      alert.classList.add(bgcstyle)
      //问题:多次添加后会两个都添加导致类名冲突,解决,可以在提示框消失后移除类名
      
      //4.过两秒后提示框消失
      setTimeout(()=>{
        alert.classList.remove('show')
        alert.classList.remove(bgcstyle)
      },2000)
    }

    //1.1登录绑定点击事件
    document.querySelector('.btn-login').addEventListener('click',()=>{
      //获取用户名和密码
      // const username = document.querySelector('.username').value
      // const password = document.querySelector('.password').value
      const form = document.querySelector('.login-form')
      const data = serialize(form,{hash:true,empty:true})
      console.log(data);//{username: '', password: ''}

      //对象结构
      const {username,password} = data
      //判断长度
      if(username.length<8){
        alertFun('用户名必须大于等于8位',false)
        console.log('用户名必须大于等于8位');
        //如果不符合,直接提前结束
        return
      }
      if(password.length<6){
        alertFun('密码必须大于等于6位',false)
        console.log('密码必须大于等于6位');
        //如果不符合,直接提前结束
        return
      }
      //axios提交用户名和密码
      axios({
        url:'http://hmajax.itheima.net/api/login',
        method:'post',
        data:{
          username,
          password
        }
      }).then(result => {
        alertFun(result.data.message,true)
        console.log(result.data.message);
      }).catch(error =>{ //返回错误信息
        alertFun(error.response.data.message,false)
        console.log(error.response.data.message);
      })
      
    })
  </script>
</body>

二、AJAX 综合案例

2.1案例 - 图书管理

在这里插入图片描述

2.1.1Bootstrap 弹框

在这里插入图片描述
在这里插入图片描述

2.1.1.1通过属性控制弹框的显示或隐藏
<body>
  <!-- 
    目标:使用Bootstrap弹框
    1. 引入bootstrap.css 和 bootstrap.js
    2. 准备弹框标签,确认结构
    3. 通过自定义属性,控制弹框的显示和隐藏
   -->

   <!-- 3. 通过bootstrp自定义属性,控制弹框的显示和隐藏 -->
  <button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target=".mybox">
    显示弹框
  </button>
  <!-- 从bootstrp获取弹框标签
    bootstrap的modal弹框:添加modal类名(默认隐藏)
   -->
   
  <div class="modal mybox" tabindex="-1">
    <div class="modal-dialog">
      <div class="modal-content">
        <!-- 弹框头部 -->
        <div class="modal-header">
          <h5 class="modal-title">Modal title</h5>
          <!-- 绑定关闭属性data-bs-dismiss="modal" -->
          <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>
2.1.1.2通过JS控制弹框的显示或隐藏
<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>
    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);
      //3.隐藏弹框
      modal.hide()
    })
  </script>
</body>

2.1.2图书管理 - 渲染列表

在这里插入图片描述

2.1.3图书管理 - 新增图书

在这里插入图片描述

2.1.4图书管理 - 删除图书

在这里插入图片描述

2.1.4 图书管理 - 编辑图书

在这里插入图片描述

2.1.5核心步骤 - 渲染数据

在这里插入图片描述

2.1.6核心步骤 - 新增数据

在这里插入图片描述

2.1.7核心步骤 - 删除数据

在这里插入图片描述

2.1.8核心步骤 - 编辑数据

在这里插入图片描述
在这里插入图片描述

/**
 * 目标1:渲染图书列表
 *  1.1 获取数据
 *  1.2 渲染数据
 */
//每一次操作都要渲染列表,所以把渲染列表封装成一个函数

const creator = '张三'
function getBookList(){
    //1.获取数据
    axios({
        url:'http://hmajax.itheima.net/api/books',
        params:{
            creator
        }
    }).then(result => {
        console.log(result.data.data); //返回一个数组
        //渲染数据
        const bookList = result.data.data
        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.1新增弹框->显示和隐藏
2.2收集表单数据,并提交到服务器保存
2.3刷新图书列表
*/ 

//点击关闭和添加都是直接显示或者隐藏,可以通过属性控制弹框
//但是点击保存还需要存数据到服务器,所以要通过JS控制隐藏

const addModalDom = document.querySelector('.add-modal') //不要写在点击监听里面
const addModal = new bootstrap.Modal(addModalDom)
//保存按钮
document.querySelector('.add-btn').addEventListener('click',()=>{

    //获取表单值通过serialize插件
    const addForm = document.querySelector('.add-form') 
    const data = serialize(addForm,{hash:true,empty:true})
    console.log(data); //{bookname: '111', author: '222', publisher: '333'}
    //对象解构
    const { bookname,author,publisher } = data
    axios({
        url:'http://hmajax.itheima.net/api/books',
        //提交用post
        method:'post',
        data:{
            bookname,
            author,
            publisher,
            creator
        }
    }).then(result => {
        console.log(result.data.message); //添加图书成功
        //对结果渲染
        getBookList()
        //保存成功后再次新增上一次的数据还在表单中,需要对表单进行重置
        addForm.reset()
        //隐藏
        addModal.hide()
        
    })

})

// const editModalDom = document.querySelector('.edit-modal') //不要写在点击监听里面
// const editModal = new bootstrap.Modal(editModalDom)

/*
需求三:删除
3.1绑定点击事件->获取图书id
3.2提交图书id->调用删除接口删除图书
3.3刷新图书列表
*/ 
//删除元素->绑定点击(注意:删除是动态的,不能直接绑定,用事件委托绑定上级)
document.querySelector('.list').addEventListener('click',e=>{
    //获取触发事件的目标元素
    //判断点击的是删除还是编辑,所以不能用 e.target.tagName = span
    if(e.target.classList.contains('del')){
        console.log('点击删除');
        //怎么获取id?再添加列表的时候给他一个自定义属性 data-id = ${item.id}
        //获取图书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 => {
            //对结果重新渲染
            getBookList()
        })
    }

    /*
    需求4:点击编辑,跳出弹框,点击修改,修改完成,隐藏弹框
    */
    const editModalDom = document.querySelector('.edit-modal') //不要写在点击监听里面
    const editModal = new bootstrap.Modal(editModalDom)
    //判断点击的是否为编辑元素
    if(e.target.classList.contains('edit')){
    console.log('点击编辑');
    //2.点击编辑,显示弹框
    editModal.show()
    //获取图书id(自定义属性id)
    const theId = e.target.parentNode.dataset.id
    
    
    //获取当前编辑图书数据->回显到编辑表单中
    axios({
        url:`http://hmajax.itheima.net/api/books/${theId}`
    }).then(result => {
        console.log(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]
        })
    })

    document.querySelector('.edit-btn').addEventListener('click',()=>{
    //获取表单值通过serialize插件
    const editForm = document.querySelector('.edit-form') 
    const editdata = serialize(editForm,{hash:true,empty:true})
    console.log(editdata.id);//{id: '', bookname: '', author: '', publisher: ''}
    //对象解构
    const { id,bookname,author,publisher} = editdata
    axios({
        url:`http://hmajax.itheima.net/api/books/${id}`,
        method:'PUT',
        data:{
            author,
            bookname,
            publisher,
            creator
        }
    }).then(result => {
        console.log(result.data.message); //修改图书成功
        //对结果重新渲染
        getBookList()
        
    })
    
    //隐藏弹框
    editModal.hide()
    })  
}
})

2.2图片上传

本地文件上传到服务器
从服务器获取url显示到页面
在这里插入图片描述

<body>
  <!-- 文件选择元素 -->
  <input type="file" class="upload">
  <img class="my-img" src="" alt="">

  <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
  <script>
    /**
     * 目标:图片上传,显示到网页上
     *  1. 获取图片文件
     *  2. 使用 FormData 携带图片文件
     *  3. 提交到服务器,获取图片url网址使用
    */
   //1.获取文件选择元素->绑定chang改变事件
   document.querySelector('.upload').addEventListener('change',e => {
    console.log(e.target); //<input type="file" class="upload">
    console.log(e.target.files);
    //拿到用户选择的图片文件
    console.log(e.target.files[0]);
    console.log(e.target.files[0].name);
    //提交到服务器保存(使用FormData内置对象)携带图片文件
    const fd = new FormData()
    //img是接口文档规定的参数名
    //append是FormData内置对象的内置属性
    fd.append('img',e.target.files[0])
    //3.提交到服务器
    axios({
      url:'http://hmajax.itheima.net/api/uploadimg',
      method:'POST',
      //传的数据是fd
      data: fd
    }).then(result => {
      console.log(result);
      //取出图片url网址,用img标签加载显示
      const imgUrl = result.data.data.url
      document.querySelector('.my-img').src = imgUrl
    })

   })
    
  </script>
</body>

在这里插入图片描述

图片返回的url网址
在这里插入图片描述
img的src是图片返回的url网址
在这里插入图片描述

2.3案例 - 网站换肤

在这里插入图片描述

/**
 * 目标:网站-更换背景
 *  1. 选择图片上传,设置body背景
 *  2. 上传成功时,"保存"图片url网址
 *  3. 网页运行后,"获取"url网址使用
 * */
//1. 选择图片上传,设置body背景
document.querySelector('.bg-ipt').addEventListener('change',e=>{
    console.log(e.target.files[0]);
    
    //要求传递表单数据类型
    const fd = new FormData()
    fd.append('img',e.target.files[0])

    //提交到服务器
    axios({
        url:'http://hmajax.itheima.net/api/uploadimg',
        method:'POST',
        //传的数据是fd
        data: fd
    }).then(result => {
        console.log(result);
        //图片在服务器上的地址
        const imgUrl = result.data.data.url
        //获取body,然后更改背景
        document.body.style.backgroundImage = `url(${imgUrl})`
    

        //2. 上传成功时,"保存"图片url网址 localStorage本地存储,并且关闭不回丢失
        localStorage.setItem('bgimg',imgUrl)
    })
})
//3. 网页运行后,"获取"url网址使用
const bgUrl = localStorage.getItem('bgimg')
console.log(bgUrl);
//bug:第一次打开网页时没有上传图片所以会有找不到图片的错误
//解决:逻辑与  //当bgUrl为空即false时不执行后面的代码

//bgUrl && document.body.style.backgroundImage = `url(${bgUrl})`
//又因为逻辑运算符优先级高于等号赋值运算符
//解决:括号包起来
bgUrl && (document.body.style.backgroundImage = `url(${bgUrl})`)

2.4案例 - 个人信息设置

2.4.1个人信息设置

在这里插入图片描述

2.4.2个人信息设置 - 信息渲染

在这里插入图片描述

//刚打开就要渲染默认信息,所以先请求信息
axios({
    url:'http://hmajax.itheima.net/api/settings',
    method:'GET',
    params:{
        creator:'张三'
    }
}).then(result => {
    console.log(result);
})

在这里插入图片描述

2.4.3个人信息设置 - 头像修改

在这里插入图片描述

2.4.4个人信息设置 - 信息修改

在这里插入图片描述

2.4.5个人信息设置 - 提示框

在这里插入图片描述

/**
 * 需求1:信息渲染
 *  1.1 获取用户的数据
 *  1.2 回显数据到标签上
 * */

//刚打开就要渲染默认信息,所以先请求信息
axios({
    url:'http://hmajax.itheima.net/api/settings',
    method:'GET',
    params:{
        creator:'张三'
    }
}).then(result => {
    console.log(result);
    console.log(result.data.data);//{avatar: 'http://hmajax.itheima.net/avatar/avatar1.png', nickname: 'itheima', email: 'itheima@itcast.cn', desc: '我是张三', gender: 0}
    const userObj = result.data.data
    //1.2 回显数据到标签上
    //数据对象“属性”和标签“类名一致”
    //遍历数组对象,使用属性去获取对应的标签,快速赋值
    const keys = Object.keys(userObj)
    console.log(keys); // ['avatar', 'nickname', 'email', 'desc', 'gender']
    keys.forEach(key => {
        //性别和头像要特殊处理
        if(key === 'avatar'){
            //赋予默认的头像
            document.querySelector('.prew').src = userObj.avatar
        }else if(key === 'gender'){
            //赋予默认性别,
            //获取性别单选框[男radio元素,女radio元素]伪数组
            //需要改标签添加checked属性
            const genderList = document.querySelectorAll('.gender')
            //获取性别数字:0:男,1女
            const gNum = userObj[key]
            //console.log(gNum);
            //通过性别的数字作为下标,找到对应性别单选框,设置选中状态
            genderList[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() //表单数据对象
    //img是接口文档规定的参数名
    //append是FormData内置对象的内置属性
    fd.append('avatar',e.target.files[0])
    fd.append('creator','张三')
    //提交到服务器
    axios({
        url:'http://hmajax.itheima.net/api/avatar',
        method:'PUT',
        //上传数据用data
        data: fd
    }).then(result => {
        console.log(result);
        //上传图片后还需要从服务器获取图片渲染
        const imgUrl = result.data.data.avatar
        //把新的头像回显到页面上
        document.querySelector('.prew').src = imgUrl
    })
})

/**
 * 需求3:提交表单
 *  3.1 收集表单信息
 *  3.2 提交服务器保存
 * */
document.querySelector('.submit').addEventListener('click',()=>{
    //3.1 收集表单信息,准备数据
    const userForm = document.querySelector('.user-form')
    const userObj = serialize(userForm,{hash:true,empty:true})
    console.log(userObj);
    //3.2提交到服务器保存
    //接口文档规定需要5个参数,userObj还差一个creator
    userObj.creator = '张三'
    //接口文档规定gender要传integer类型的数据。性别字数字符串转成
    userObj.gender = + userObj.gender
    //3.2 提交服务器保存
    axios({
        url:'http://hmajax.itheima.net/api/settings',
        method:'PUT',
        //内容格式: JSON
        data:userObj
    }).then(result => {
        //创建对象,显示操作成功
    const toastDom = document.querySelector('.toast')
    //用的是bootstrap内置的Toast属性
    const toast = new bootstrap.Toast(toastDom)
    //显示弹框
    toast.show()
    })
    
})

三、AJAX 原理

3.1XMLHttpRequest

3.1.1AJAX原理 - XMLHttpRequest

定义:
在这里插入图片描述
**关系:**axios 内部采用 XMLHttpRequest 与服务器交互
在这里插入图片描述
**好处:**掌握使用 XHR 与服务器进行数据交互,了解 axios 内部原理

3.1.2使用 XMLHttpRequest

步骤:
1. 创建 XMLHttpRequest 对象(new)
2. 配置请求方法和请求 url 地址
3. 监听 loadend 事件,接收响应结果
4. 发起请求
在这里插入图片描述
在这里插入图片描述

3.1.3总结:

  1. AJAX 原理是什么?
    ➢ XMLHttpRequest 对象
  2. 为什么学习 XHR?
    ➢ 有更多与服务器数据通信方式
    ➢ 了解 axios 内部原理
  3. XHR 使用步骤?
    ➢ 创建 XHR 对象
    ➢ 调用 open 方法,设置 url 和请求方法
    ➢ 监听 loadend 事件,接收结果
    ➢ 调用 send 方法,发起请求
    在这里插入图片描述

3.1.4XMLHttpRequest - 查询参数

定义:浏览器提供给服务器的额外信息,让服务器返回浏览器想要的数据
语法:http://xxxx.com/xxx/xxx?参数名1=值1&参数名2=值2
在这里插入图片描述

3.1.5案例 地区查询

需求:输入省份和城市名字,查询地区列表
请求地址:http://hmajax.itheima.net/api/area?参数名1=值1&参数名2=值2
在这里插入图片描述

//3.组织查询参数字符串(把参数转成字符串写入请求地址中)
      const qObj = {
        pname:provinceName,
        cname:cityName
      }
      //4.查询参数对象->查询参数字符串
      const paramsObj = new URLSearchParams(qObj)
      const queryString = paramsObj.toString()
      console.log(queryString); //pname=%E5%8C%97%E4%BA%AC&cname=%E5%8C%97%E4%BA%AC%E5%B8%82
<body>
  <div class="container">
    <form id="editForm" class="row">
      <!-- 输入省份名字 -->
      <div class="mb-3 col">
        <label class="form-label">省份名字</label>
        <input type="text" value="北京" name="province" class="form-control province" placeholder="请输入省份名称" />
      </div>
      <!-- 输入城市名字 -->
      <div class="mb-3 col">
        <label class="form-label">城市名字</label>
        <input type="text" value="北京市" name="city" class="form-control city" placeholder="请输入城市名称" />
      </div>
    </form>
    <button type="button" class="btn btn-primary sel-btn">查询</button>
    <br><br>
    <p>地区列表: </p>
    <ul class="list-group">
      <!-- 示例地区 -->
      <li class="list-group-item">东城区</li>
    </ul>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
  <script>
    /**
     * 目标: 根据省份和城市名字, 查询对应的地区列表
    */
       //需求:根据省份和城市的名字,查询地区列表
    //1.查询按钮--绑定点击事件
    document.querySelector('.sel-btn').addEventListener('click', () => {
      console.log('点击');
      //获取用户输入省份和城市名字
      let provinceName = document.querySelector('.province').value
      let cityName = document.querySelector('.city').value
      console.log(provinceName, cityName);


      //3.组织查询参数字符串
      const qObj = {
        pname:provinceName,
        cname:cityName
      }
      //4.查询参数对象->查询参数字符串
      const paramsObj = new URLSearchParams(qObj)
      const queryString = paramsObj.toString()
      console.log(queryString); //pname=%E5%8C%97%E4%BA%AC&cname=%E5%8C%97%E4%BA%AC%E5%B8%82
      /**
        * 目标:使用XMLHttpRequest对象与服务器通信
        *  1. 创建 XMLHttpRequest 对象
        *  2. 配置请求方法和请求 url 地址
        *  3. 监听 loadend 事件,接收响应结果
        *  4. 发起请求
       */
      //1. 创建 XMLHttpRequest 对象
      const xhr = new XMLHttpRequest()
      xhr.open('GET', `http://hmajax.itheima.net/api/area?${queryString}`)
      //3. 监听 loadend 事件,接收响应结果
      xhr.addEventListener('loadend', () => {
        console.log(xhr.response);
        console.log(JSON.parse(xhr.response));
        
        console.log(JSON.parse(xhr.response).list);//包含各地区的一个数组
        //遍历数组,添加标签返回一个字符串
        let cityLi = JSON.parse(xhr.response).list.map(areaName => `<li class="list-group-item">${areaName}</li>`)
        console.log(cityLi);//map返回的是一个字符串
        //列表的上一级添加内容
        document.querySelector('.list-group').innerHTML = cityLi.join('')
        
      })
      //4. 发起请求
      xhr.send()
    })
  </script>
</body>

在这里插入图片描述

3.1.6XMLHttpRequest - 数据提交

需求:通过 XHR 提交用户名和密码,完成注册功能
核心:
请求头设置 Content-Type:application/json
请求体携带 JSON 字符串
在这里插入图片描述
在这里插入图片描述

```javascript
  <script>
    /**
     * 目标:使用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);
      })
      //设置请求头-告诉服务器内容类型(JSON字符串)
      xhr.setRequestHeader('Content-Type','application/json')
      //准备提交数据
      const userObj = {
        username:'itheima00007',
        password:'7654321'
      }
      //要求传的是JSON字符串,所以要把传的数据转成json字符串
      const userStr = JSON.stringify(userObj)
      //设置请求体,发起请求
      xhr.send(userStr)
    })
  </script>

服务器返回的结果
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.2Promise(承诺)

定义:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise
Promise对象用于表示一个异步操作的最终完成(或失败)及其结果
在这里插入图片描述
在这里插入图片描述

总结:

  1. 什么是 Promise?
    ➢ 表示(管理)一个异步操作最终状态和结果值的对象
  2. 为什么学习 Promise?
    ➢ 成功和失败状态,可以关联对应处理程序
    ➢ 了解 axios 内部原理
  3. Promise 使用步骤?
    在这里插入图片描述

Promise - 三种状态:

作用:了解Promise对象如何关联处理函数,以及代码执行顺序
**概念:**一个Promise对象,必然处于以下几种状态之一
✓ 待定(pending) :初始状态,既没有被兑现,也没有被拒绝
✓ 已兑现(fulfilled) :意味着,操作成功完成
✓ 已拒绝(rejected) :意味着,操作失败
在这里插入图片描述
**注意:**Promise对象一旦被兑现/拒绝就是已敲定了,状态无法再被改变

<body>
  <script>
    /**
     * 目标:使用Promise管理异步任务
    */
   //1.创建promise对象 (处于pending-待定状态)
   const p = new Promise((resolve, reject) => {
    //Promise对象创建时,这里的代码都会执行了
    //2.执行异步代码
    setTimeout(()=>{
      //(resolve()=> fulfilled状态-已兑现 => then() )
      //resolve('模拟AJAX请求-成功结果') //成功

      //(reject()=> rejected状态-已拒绝 => catch() )
      reject(new Error('模拟AJAX请求-失败结果')) //失败
    },2000)
   })
   
   //3.获取结果
  p.then(result => {
    console.log(result); //模拟AJAX请求-成功结果
  }).catch(error => {
    console.log(error); //Error: 模拟AJAX请求-失败结果
  })

  console.log(p);
  </script>
</body>

总结:

  1. Promise 对象有哪 3 种状态?
    ➢ 待定 pending
    ➢ 已兑现 fulfilled
    ➢ 已拒绝 rejected
  2. Promise 状态有什么用?
    ➢ 状态改变后,调用关联的处理函数
    在这里插入图片描述

3.2.1案例: 使用Promise + XHR 获取省份列表

**需求:**使用 Promise 管理 XHR 获取省份列表,并展示到页面上
步骤:

  1. 创建 Promise 对象
  2. 执行 XHR 异步代码,获取省份列表
  3. 关联成功或失败函数,做后续处理
    在这里插入图片描述
<body>
  <p class="my-p"></p>
  <script>
    /**
     * 目标:使用Promise管理XHR请求省份列表
     *  1. 创建Promise对象
     *  2. 执行XHR异步代码,获取省份列表
     *  3. 关联成功或失败函数,做后续处理
    */
   //1. 创建Promise对象
    const promise = 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); //xhr有个属性是:status:200
        if(xhr.status >=200 && xhr.status <300){
          //成功获取的数据
          resolve(JSON.parse(xhr.response))
        }else{
          //失败获取的数据
          reject(new Error(xhr.response))
        }
        //console.log(xhr.response);
      })
      xhr.send()
   })

   //3. 关联成功或失败函数,做后续处理
   promise.then(result => {
    console.log(result);
    //result.list包含数组省份
    document.querySelector('.my-p').innerHTML = result.list.join('<br>')
   }).catch(error => {
    console.log(error);
    //错误对象要用console.dir详细打印
    console.dir(error);
    document.querySelector('.my-p').innerHTML = error.message
   })
    
  </script>
</body>

3.3封装简易版 axios

3.3.1 封装_简易axios_获取省份列表

需求:基于 Promise + XHR 封装 myAxios 函数,获取省份列表展示
步骤:

  1. 定义 myAxios 函数,接收配置对象,返回 Promise 对象
  2. 发起 XHR 请求,默认请求方法为 GET
  3. 调用成功/失败的处理程序
  4. 使用 myAxios 函数,获取省份列表展示
    在这里插入图片描述
<body>
  <script>
    /**
     * 目标:封装_简易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',()=>{
        //3. 调用成功/失败的处理程序
        if(xhr.status >=200 && xhr.status < 300){
          resolve(JSON.parse(xhr.response))
        }else{
          reject(new Error(xhr.response))
        }
      })
      //发起请求
      xhr.send()
    })
   }
//4. 使用myAxios函数,获取省份列表展示
myAxios({
  url:'http://hmajax.itheima.net/api/province' 
}).then(result => {
  console.log(result);
}).catch(error => {
  console.log(error);
})

  </script>
</body>

3.3.1 封装_简易axios_获取省份列表

需求:修改 myAxios 函数支持传递查询参数,获取"辽宁省","大连市"对应地区列表展示
步骤:

  1. myAxios 函数调用后,判断 params 选项
  2. 基于 URLSearchParams 转换查询参数字符串
  3. 使用自己封装的 myAxios 函数展示地区列表
    在这里插入图片描述
<body>
  <p class="my-p"></p>
  <script>
    /**
     * 目标:封装_简易axios函数_获取地区列表
     *  1. 判断有params选项,携带查询参数
     *  2. 使用URLSearchParams转换,并携带到url上
     *  3. 使用myAxios函数,获取地区列表
    */
    function myAxios(config) {
      return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest()

        //下面需要用到url,所以在这之前要判断是否又pramse属性,并对config.params处理拼接到url后面再传入
        if(config.params){
          //2. 使用URLSearchParams转换,并携带到url上
          const paramsObj = new URLSearchParams(config.params) 
          const querySring = paramsObj.toString()
          //把查询参数字符串拼接在url?后面
          config.url += `?${querySring}`
        }
        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);
    })

  </script>
</body>

3.3.1 封装_简易axios_注册用户

需求:修改 myAxios 函数支持传递请求体数据(data),完成注册用户功能
步骤:

  1. myAxios 函数调用后,判断 data 选项
  2. 转换数据类型,在 send 方法中发送
  3. 使用自己封装的 myAxios 函数完成注册用户功能
    在这里插入图片描述
<body>
  <p class="my-p"></p>
  <button class="btn">注册用户</button>
  <script>
    /**
     * 目标:封装_简易axios函数_注册用户
     *  1. 判断有data选项,携带请求体
     *  2. 转换数据类型,在send中发送
     *  3. 使用myAxios函数,完成注册用户
    */
    function myAxios(config) {
      return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest()

        //下面需要用到url,所以在这之前要判断是否又pramse属性,并对config.params处理拼接到url后面再传入
        if(config.params){
          //2. 使用URLSearchParams转换,并携带到url上
          const paramsObj = new URLSearchParams(config.params) 
          const querySring = paramsObj.toString()
          //把查询参数字符串拼接在url?后面
          config.url += `?${querySring}`
        }
        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))
          }
        })
        //1. 判断有data选项,携带请求体
        //如果有请求体数据就传
        if(config.data){
          //2. 转换数据类型,在send中发送,转成json字符串 Body 参数 (application/json)
          const jsonStr = JSON.stringify(config.data)

          xhr.setRequestHeader('Content-Type','application/json')
          xhr.send(jsonStr)
        }else{ //没有请求体数据的话就传空
          xhr.send()
        }
        
      })
    }
    //查询地区列表
    document.querySelector('.btn').addEventListener('click',()=>{
      myAxios({
      url:'http://hmajax.itheima.net/api/register',
      method:'POST',
      data:{
        username:'12345678910111',
        password:'654321'
      }
      
    }).then(result => {
      console.log(result);
    }).catch(error =>{
      //一般使用dir来打印错误
      console.dir(error)
    })
    })
  </script>
</body>

在这里插入图片描述

3.4案例 - 天气预报

步骤:

  1. 获取北京市天气数据,展示
  2. 搜索城市列表,展示
  3. 点击城市,显示对应天气数据
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

天气预报 - 搜索城市列表
需求:根据关键字,展示匹配城市列表
步骤:

  1. 绑定 input 事件,获取关键字
  2. 获取展示城市列表数据
    在这里插入图片描述

天气预报 - 展示城市天气
需求:展示用户搜索查看的城市天气
步骤:

  1. 检测搜索列表点击事件,获取城市 code 值
  2. 复用获取展示城市天气函数
    在这里插入图片描述
/**
 * 目标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 weatherObj = result.data
        //1.2 数据展示到页面
        //阳历和农历日期,都在一个div里面,所以用模板字符串不用频繁获取
        const dataStr = `<span class="dateShort">${weatherObj.date}</span>
        <span class="calendar">农历&nbsp;
          <span class="dateLunar">${weatherObj.dateLunar}</span>
        </span>` 
        document.querySelector('.title').innerHTML = dataStr
        document.querySelector('.area').innerHTML = weatherObj.area
        const weatherBox = `<div class="tem-box">
        <span class="temp">
          <span class="temperature">${weatherObj.temperature}</span>
          <span>°</span>
        </span>
      </div>
      <div class="climate-box">
        <div class="air">
          <span class="psPm25">${weatherObj.psPm25}</span>
          <span class="psPm25Level">${weatherObj.psPm25Level}</span>
        </div>
        <ul class="weather-list">
          <li>
            <img src=${weatherObj.weatherImg} class="weatherImg" alt="">
            <span class="weather">${weatherObj.weather}</span>
          </li>
          <li class="windDirection">${weatherObj.windDirection}</li>
          <li class="windPower">${weatherObj.windPower}</li>
        </ul>
      </div>
        `
        document.querySelector('.weather-box').innerHTML = weatherBox
        //当日天气
        const todayWStr = `
        
      <div class="range-box">
        <span>今天:</span>
        <span class="range">
          <span class="weather">${weatherObj.todayWeather.weather}</span>
          <span class="temNight">${weatherObj.todayWeather.temNight}</span>
          <span>-</span>
          <span class="temDay">${weatherObj.todayWeather.temDay}</span>
          <span>℃</span>
        </span>
      </div>
      <ul class="sun-list">
        <li>
          <span>紫外线</span>
          <span class="ultraviolet">${weatherObj.todayWeather.ultraviolet}</span>
        </li>
        <li>
          <span>湿度</span>
          <span class="humidity">${weatherObj.todayWeather.humidity}</span>%
        </li>
        <li>
          <span>日出</span>
          <span class="sunriseTime">${weatherObj.todayWeather.sunriseTime}</span>
        </li>
        <li>
          <span>日落</span>
          <span class="sunsetTime">${weatherObj.todayWeather.sunsetTime}</span>
        </li>
      </ul>
        `
        document.querySelector('.today-weather').innerHTML = todayWStr
    
        //7天天气预报
        const dayForecast = weatherObj.dayForecast
        const sevenWStr = 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('<br>')

        console.log(sevenWStr);
        document.querySelector('.week-wrap').innerHTML = sevenWStr
    })
}
//默认进入网页-就要获取天气数据,接口文档规定传的是城市编码(北京市城市编码:'110100')
getWeather('110100')

/*
需求二:搜索城市列表
2.1绑定input事件,获取关键字
2.2获取展示城市列表数据

*/ 
//2.1绑定input事件(监听输入),获取关键字
document.querySelector('.search-city').addEventListener('input',(e)=>{
    console.log(e.target.value);
    //2.2获取展示城市列表数据
    myAxios({
        url:'http://hmajax.itheima.net/api/weather/city',
        params:{
            city:e.target.value
        }
    }).then(result => {
        console.log(result);
        console.log(result.data);//包含所有含有"北"字地区的数组
        const cityItem = result.data.map(item => {
            return `<li class="city-item" data-code='${item.code}'>${item.name}</li>`
        }).join('')
        document.querySelector('.search-list').innerHTML = cityItem
    })
})

/*
需求三:切换城市天气
3.1给li绑定城市点击事件,获取城市code值
li是动态的,想给li绑定事件需要用到事件委托
3.2获取获取并展示天气的函数

*/ 
//3.1给li绑定城市点击事件,获取城市code值(li是动态的,想给li绑定事件需要用到事件委托)
document.querySelector('.search-list').addEventListener('click',e => {
    if(e.target.classList.contains('city-item')){
        //在绑定input事件时给li添加data-code自定义属性,这一步可以获取自定义 属性的code的值
        //然后得到是哪个城市,再调用渲染函数传递值
        const cityCode = e.target.dataset.code
        //3.2获取获取并展示天气的函数
        getWeather(cityCode)
    }
})

四、AJAX进阶

4.1同步代码和异步代码

4.1.1同步代码和异步代码

同步代码:https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Asynchronous/Introducing#%E5%90%8C%E6%AD%A5%E7%BC%96%E7%A8%8B
在这里插入图片描述
异步代码:https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Asynchronous/Introducing
在这里插入图片描述
同步代码:逐行执行,需原地等待结果后,才继续向下执行
异步代码:调用后耗时,不阻塞代码继续执行(不必原地等待),在将来完成后触发一个回调函数

4.1.2同步和异步

在这里插入图片描述

4.1.3总结

  1. 什么是同步代码?
    ➢ 逐行执行,原地等待结果后,才继续向下执行
  2. 什么是异步代码?
    ➢ 调用后耗时,不阻塞代码执行,将来完成后触发回调函数
  3. JS 中有哪些异步代码?
    ➢ setTimeout / setInterval
    ➢ 事件
    ➢ AJAX
  4. 异步代码如何接收结果?
    ➢ 依靠回调函数来接收

4.2回调函数地狱和 Promise 链式调用

4.2.1回调函数地狱

需求:展示默认第一个省,第一个城市,第一个地区在下拉菜单中
概念:在回调函数中嵌套回调函数,一直嵌套下去就形成了回调函数地狱
缺点:可读性差,异常无法捕获,耦合性严重,牵一发动全身
在这里插入图片描述
在这里插入图片描述

<body>
  <form>
    <span>省份:</span>
    <select>
      <option class="province"></option>
    </select>
    <span>城市:</span>
    <select>
      <option class="city"></option>
    </select>
    <span>地区:</span>
    <select>
      <option class="area"></option>
    </select>
  </form>
  <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
  <script>
    /**
     * 目标:演示回调函数地狱
     * 需求:获取默认第一个省,第一个市,第一个地区并展示在下拉菜单中
     * 概念:在回调函数中嵌套回调函数,一直嵌套下去就形成了回调函数地狱
     * 缺点:可读性差,异常无法获取,耦合性严重,牵一发动全身
    */
   //1.获取默认第一个省份的名字
   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:pname}}).then(result =>{
      console.log(result);
      const cname = result.data.list[0]
      document.querySelector('.city').innerHTML = cname
      //3.获取默认第一个地区的名字
      axios({url:'http://hmajax.itheima.net/api/area1',params:{pname:pname,cname:cname}}).then(result => {
        console.log(result);
        document.querySelector('.area').innerHTML = result.data.list[0]
      })

    })

   }).catch(error => {
    console.log(error);
   })
    
  </script>
</body>

在这里插入图片描述
可以看到获取地区url错误在外层的catch无法获取,并且代码可读性也很差,稍微不留神就会弄错

4.2.2总结

  1. 什么是回调函数地狱?
    ➢ 在回调函数一直向下嵌套回调函数,形成回调函数地狱
  2. 回调函数地狱问题?
    ➢ 可读性差
    ➢ 异常捕获困难
    ➢ 耦合性严重

4.2.3Promise - 链式调用

概念:依靠 then() 方法会返回一个新生成的 Promise 对象特性,继续串联下一环任务,直到结束
细节:then() 回调函数中的返回值,会影响新生成的 Promise 对象最终状态和结果
好处:通过链式调用,解决回调函数嵌套问题

在这里插入图片描述

<body>
  <script>
    /**
     * 目标:掌握Promise的链式调用
     * 需求:把省市的嵌套结构,改成链式调用的线性结构
    */
   //1.创建Promise对象-模拟请求省份名字
   const p= new Promise((resolve,reject) => {
    setTimeout(() => {
      resolve('北京')
    },2000)
   })
   
   //2.获取省份名字
   const p2 = p.then(result => {
    console.log(result);
    //3.创建Promise对象-模拟请求城市名字
    //return 的Promise对象最终状态和结果会影响新的promise对象
    return new Promise((resolve,reject) => {
      setTimeout(() => {
        resolve(result + '--北京市')
      },2000)
    })
   })
   //then()原地的结果是一个新的Promise对象
   console.log(p2 === p); //false ,p2不等于p,说明他俩值虽然相同但是地址不同,p2是新new的一个对象
  

   //4.获取里面的城市名字
  const p3 = p2.then(result => {
    console.log(result);
    //5.创建Promise对象-模拟请求地区名字
    return new Promise((resolve,reject) => {
      setTimeout(()=>{
        resolve(result + '--东城区')
      })
    })
  
   })
   //获取里面的地区名字
   p3.then(result => {
    console.log(result);
   })
  </script>
</body>

在这里插入图片描述
在这里插入图片描述

4.2.4总结

  1. 什么是 Promise 的链式调用?
    ➢ 使用 then 方法返回新 Promise 对象特性,一直串联下去
  2. then 回调函数中,return 的值会传给哪里?
    传给 then 方法生成的新 Promise 对象
  3. Promise 链式调用有什么用?
    ➢ 解决回调函数嵌套问题

4.2.5Promise 链式应用

目标:使用 Promise 链式调用,解决回调函数地狱问题
做法:每个 Promise 对象中管理一个异步任务,用 then 返回 Promise 对象,串联起来
在这里插入图片描述

<body>
  <form>
    <span>省份:</span>
    <select>
      <option class="province"></option>
    </select>
    <span>城市:</span>
    <select>
      <option class="city"></option>
    </select>
    <span>地区:</span>
    <select>
      <option class="area"></option>
    </select>
  </form>
  <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
  <script>
    /**
     * 目标:把回调函数嵌套代码,改成Promise链式调用结构
     * 需求:获取默认第一个省,第一个市,第一个地区并展示在下拉菜单中
    */
   //1.得到-获取省份Promise对象
   let pname = ''
   axios({url:'http://hmajax.itheima.net/api/province'}).then(result => {
    //console.log(result);
    pname = result.data.list[0]
    document.querySelector('.province').innerHTML = pname
    //2.得到-获取城市的promise对象
    return axios({url:'http://hmajax.itheima.net/api/city',params:{ pname }})
   }).then(result => {
    //获取城市列表
    console.log(result);
    const cname = result.data.list[0]
    document.querySelector('.city').innerHTML = cname
    //2.得到-获取地区的promise对象
    //注意:这里的pname跟上面的panme是兄弟,不能直接访问,可以给panme一个全局变量
    return axios({url:'http://hmajax.itheima.net/api/area',params:{ pname , cname}})
   }).then(result => {
    console.log(result);
    document.querySelector('.area').innerHTML = result.data.list[0]
   })

    
  </script>
</body>

4.3async 和 await 使用(异步编程的终极解决方案)

4.3.1async函数和await

定义:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/async_function
在这里插入图片描述
概念: 在 async 函数内,使用 await 关键字取代 then 函数,等待获取 Promise 对象成功状态的结果值
在这里插入图片描述

<body>
  <form>
    <span>省份:</span>
    <select>
      <option class="province"></option>
    </select>
    <span>城市:</span>
    <select>
      <option class="city"></option>
    </select>
    <span>地区:</span>
    <select>
      <option class="area"></option>
    </select>
  </form>
  <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
  <script>
    /**
     * 目标:掌握async和await语法,解决回调函数地狱
     * 概念:在async函数内,使用await关键字,获取Promise对象"成功状态"结果值
     * 注意:await必须用在async修饰的函数内(await会阻止"异步函数内"代码继续执行,原地等待结果)
    */
   //定义async修饰函数
   async function getData(){
    const pObj = await axios({url:'http://hmajax.itheima.net/api/province'})
    const pname = pObj.data.list[0]
    console.log(pname);

    const cObj = await axios({url:'http://hmajax.itheima.net/api/city',params:{pname}})
    const cname = cObj.data.list[0]
    console.log(cname);

    const areaObj = await axios({url:'http://hmajax.itheima.net/api/area',params:{pname,cname}})
    const areaname = areaObj.data.list[0]
    console.log(areaname);

    document.querySelector('.province').innerHTML = pname
    document.querySelector('.city').innerHTML = cname
    document.querySelector('.area').innerHTML = areaname
  }

  getData()
  </script>
</body>

4.3.2async函数和await_捕获错误

在这里插入图片描述

如果try里的某行代码报错后,try中剩余的代码就不会执行了
<body>
  <form>
    <span>省份:</span>
    <select>
      <option class="province"></option>
    </select>
    <span>城市:</span>
    <select>
      <option class="city"></option>
    </select>
    <span>地区:</span>
    <select>
      <option class="area"></option>
    </select>
  </form>
  <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
  <script>
    /**
     * 目标:掌握async和await语法,解决回调函数地狱
     * 概念:在async函数内,使用await关键字,获取Promise对象"成功状态"结果值
     * 注意:await必须用在async修饰的函数内(await会阻止"异步函数内"代码继续执行,原地等待结果)
    */
   //定义async修饰函数
   async function getData(){
    try{
      const pObj = await axios({url:'http://hmajax.itheima.net/api/province11'})
      const pname = pObj.data.list[0]
      console.log(pname);

      const cObj = await axios({url:'http://hmajax.itheima.net/api/city',params:{pname}})
      const cname = cObj.data.list[0]
      console.log(cname);

      const areaObj = await axios({url:'http://hmajax.itheima.net/api/area',params:{pname,cname}})
      const areaname = areaObj.data.list[0]
      console.log(areaname);

      document.querySelector('.province').innerHTML = pname
      document.querySelector('.city').innerHTML = cname
      document.querySelector('.area').innerHTML = areaname
    }catch(error){
      //({url:'http://hmajax.itheima.net/api/province11'})
      //如果try里的某行代码报错后,try中剩余的代码就不会执行了
      console.dir(error); //M
    }
    
  }

  getData()
  </script>
</body>

4.4事件循环-EventLoop

4.4.1认识 - 事件循环(EventLoop)

好处:掌握 JavaScript 是如何安排和运行代码的
在这里插入图片描述
运行结果都是1 3 2

4.4.2事件循环(EventLoop)

在这里插入图片描述
原因:JavaScript 单线程(某一刻只能执行一行代码),为了让耗时代码不阻塞其他代码运行,设计了事件循环模型
在这里插入图片描述

4.4.3事件循环 - 执行过程

定义:执行代码和收集异步任务的模型,在调用栈空闲,反复调用任务队列里回调函数的执行机制,就叫事件循环
在这里插入图片描述

4.4.4总结

  1. 什么是事件循环?
    ➢ 执行代码和收集异步任务,在调用栈空闲时,反复调用任务队列里
    回调函数执行机制
  2. 为什么有事件循环?
    ➢ JavaScript 是单线程的,为了不阻塞 JS 引擎,设计执行代码的模型
  3. JavaScript 内代码如何执行?
    执行同步代码,遇到异步代码交给宿主浏览器环境执行
    异步有了结果后,把回调函数放入任务队列排队
    当调用栈空闲后,反复调用任务队列里的回调函

4.4.5事件循环 - 练习

使用模型,分析代码执行过程
在这里插入图片描述
1,5,3,2,4

xhr.addEventListener('loadend', () => {
//注意:绑定监听是异步
        console.log(4)
      })

在这里插入图片描述
在这里插入图片描述

4.4.6宏任务与微任务

宏任务与微任务
ES6 之后引入了 Promise 对象, 让 JS 引擎也可以发起异步任务
异步任务分为:
宏任务:由浏览器环境执行的异步代码
微任务:由 JS 引擎环境执行的异步代码
在这里插入图片描述
Promise本身是同步的,而then和catch回调函数是异步的
先微任务再宏任务

4.4.6宏任务与微任务–执行顺序

注意:1.Promise本身是同步的,而then和catch回调函数是异步的
2.先微任务再宏任务
在这里插入图片描述

4.4.7总结

  1. 什么是宏任务?
    浏览器执行的异步代码
    ➢ 例如:JS 执行脚本事件,setTimeout/setInterval,AJAX请求完成
    事件,用户交互事件等
  2. 什么是微任务?
    ➢ JS 引擎执行的异步代码
    ➢ 例如:Promise对象.then()的回调
  3. JavaScript 内代码如何执行?
    ➢ 执行第一个 script 脚本事件宏任务,里面同步代码
    ➢ 遇到 宏任务/微任务 交给宿主环境,有结果回调函数进入对应队列
    ➢ 当执行栈空闲时,清空微任务队列,再执行下一个宏任务,从1再来

4.4.8事件循环 - 经典面试题

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.5Promise.all 静态方法

4.5.1Promise.all 静态方法

概念:合并多个 Promise 对象,等待所有同时成功完成(或某一个失败),做后续逻辑
在这里插入图片描述
语法:
在这里插入图片描述
**案例需求:**同时请求“北京”,“上海”,“广州”,“深圳”的天气并在网页尽可能同时显示
在这里插入图片描述

<body>
  <ul class="my-ul"></ul>
  <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
  <script>
    /**
     * 目标:掌握Promise的all方法作用,和使用场景
     * 业务:当我需要同一时间显示多个请求的结果时,就要把多请求合并
     * 例如:默认显示"北京", "上海", "广州", "深圳"的天气在首页查看
     * code:
     * 北京-110100
     * 上海-310100
     * 广州-440100
     * 深圳-440300
    */
   //1.请求城市的天气,得到Promise对象
   //axios获取的是一个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:'310100'}})
   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'}})

   //2.使用promise.all合并多个promise对象
   const p = Promise.all([bjPromise,shPromise,gzPromise,szPromise])
   p.then(result => {
    //结果数组的顺序跟合并时顺序是一致的
    console.log(result); //返回一个数组
    const re = result.map(item => {
      return `<li>${item.data.data.area}---${item.data.data.weather}</li>`
    })
    document.querySelector('.my-ul').innerHTML = re
   }).catch(error => {
    console.log(error);
   })
  </script>
</body>

在这里插入图片描述

4.6案例 - 商品分类

在这里插入图片描述
在这里插入图片描述

<body>
  <!-- 大容器 -->
  <div class="container">
    <div class="sub-list">
      <div class="item">
        <h3>分类名字</h3>
        <ul>
          <li>
            <a href="javascript:;">
              <img src="http://zhoushugang.gitee.io/erabbit-client-pc-static/uploads/img/category%20(9).png" />
              <p>巧克力</p>
            </a>
          </li>
          <li>
            <a href="javascript:;">
              <img src="http://zhoushugang.gitee.io/erabbit-client-pc-static/uploads/img/category%20(9).png" />
              <p>巧克力</p>
            </a>
          </li>
          <li>
            <a href="javascript:;">
              <img src="http://zhoushugang.gitee.io/erabbit-client-pc-static/uploads/img/category%20(9).png" />
              <p>巧克力</p>
            </a>
          </li>
        </ul>
      </div>
    </div>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
  <script>
    /**
     * 目标:把所有商品分类“同时”渲染到页面上
     *  1. 获取所有一级分类数据
     *  2. 遍历id,创建获取二级分类请求
     *  3. 合并所有二级分类Promise对象
     *  4. 等待同时成功后,渲染页面
    */
   //1.获取所有一级分类数据
   axios({
    url:'http://hmajax.itheima.net/api/category/top',

   }).then(result => {
    console.log(result);
    //2. 遍历id,创建获取二级分类请求
    const secPromiseList = result.data.data.map(item => {
      return axios({
        url:'http://hmajax.itheima.net/api/category/sub',
        params:{
          id:item.id //传递一级分类的id
        }
      })
    })
    console.log(secPromiseList); //[二级分类请求的Promise对象,9个]
    //3. 合并所有二级分类Promise对象
    const p = Promise.all(secPromiseList)
    p.then(result => {
      console.log(result);
      //4. 等待同时成功后,渲染页面
      const htmlStr = result.map(item => {
          const dataObj = item.data.data
          return `<div class="item">
        <h3>${dataObj.name}</h3>
        <ul>
          ${dataObj.children.map(item => {
            return `<li>
            <a href="javascript:;">
              <img src=${item.picture}>
              <p>${item.name}</p>
            </a>
          </li>`
          })}
        </ul>
      </div>`
       

      }).join('')
      document.querySelector('.sub-list').innerHTML = htmlStr
    })
  })

  </script>
</body>

4.7案例 - 学习反馈

在这里插入图片描述
在这里插入图片描述

/**
 * 目标1:完成省市区下拉列表切换
 *  1.1 设置省份下拉菜单数据
 *  1.2 切换省份,设置城市下拉菜单数据,清空地区下拉菜单
 *  1.3 切换城市,设置地区下拉菜单数据
 */
//1.1 设置省份下拉菜单数据
axios({
    url:'http://hmajax.itheima.net/api/province'

}).then(result => {
    console.log(result);
    const pStr = result.data.list.map(pname => {
        return `<option value="${pname}">${pname}</option>`
    }).join('')
    document.querySelector('.province').innerHTML = `<option value="">省份</option>` + pStr
})
//1.2 切换省份,设置城市下拉菜单数据,清空地区下拉菜单
document.querySelector('.province').addEventListener('change',async e => {
    //获取用户选择的省份名字
    console.log(e.target.value);
    //在 async 函数内,使用 await 关键字取代 then 函数,**等待**获取 Promise 对象**成功状态的结果值
    const result = await axios({
        url:'http://hmajax.itheima.net/api/city',
        params:{
            pname:e.target.value
        }
    })
    console.log(result);
    const cStr = result.data.list.map(cname => {
        return `<option value=${cname}>${cname}</option>`
    }).join('')
    document.querySelector('.city').innerHTML = `<option value="">城市</option>` + cStr

    //清空地区数据
    document.querySelector('.area').innerHTML = `<option value="">地区</option>`
})

//1.3 切换城市,设置地区下拉菜单数据
//检测城市改变
document.querySelector('.city').addEventListener('change',async e => {
    //获取用户选择的城市名字
    console.log(e.target.value);
    //在 async 函数内,使用 await 关键字取代 then 函数,**等待**获取 Promise 对象**成功状态的结果值
    const result = await axios({
        url:'http://hmajax.itheima.net/api/area',
        params:{
            pname:document.querySelector('.province').value,
            cname:e.target.value
        }
    })
    console.log(result);
    const areaStr = result.data.list.map(aname => {
        return `<option value=${aname}>${aname}</option>`
    }).join('')
    document.querySelector('.area').innerHTML = `<option value="">地区</option>` + areaStr

    //清空地区数据
    //document.querySelector('.area').innerHTML = `<option value="">地区</option>`
})

/*需求二:收集数据提交保存
2.1监听点击事件 
2.2依靠 插件收集表单数据
3.2基于axios提交保存,显示结果
*/
//2.1监听点击事件 
document.querySelector('.submit').addEventListener('click',async ()=>{
    //2.2依靠 插件收集表单数据
    const from = document.querySelector('.info-form')
    const data = serialize(from,{ hash:true,empty:true})
    console.log(data);
    try{
        //3.2基于axios提交保存,显示结果
    const result = await axios({
        url:'http://hmajax.itheima.net/api/feedback',
        method:'POST',
        data:data
    })
    console.log(result);
    alert(result.data.message)
    }catch(error) {
        console.dir(error);
        alert(error.response.data.message)
    }
    
})
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
您好!对于学习Ajax,我可以为您提供一些学习笔记。以下是一份简要的Ajax学习笔记,希望对您有所帮助: 1. 什么是Ajax? - Ajax全称为Asynchronous JavaScript and XML(异步JavaScript和XML),是一种用于创建交互式Web应用程序的技术。 - 它通过在后台与服务器进行数据交换,实现在不刷新整个页面的情况下更新部分页面内容。 2. Ajax的优点: - 异步处理:可以在后台发送和接收数据,而无需刷新整个页面。 - 提高用户体验:通过部分更新页面内容,可以提供更快的响应时间和更流畅的用户体验。 - 减轻服务器负担:只更新需要的部分内容,减少了不必要的数据传输和服务器负载。 3. Ajax的基本原理: - 使用XMLHttpRequest对象向服务器发送请求,并接收响应。 - 通过JavaScript在前端处理响应数据。 - 更新页面内容,以显示最新的数据。 4. Ajax的核心技术: - XMLHttpRequest对象:用于与服务器进行数据交换。 - JavaScript:用于处理响应数据和更新页面内容。 - XML或JSON:用于传输数据格式。 5. Ajax的使用步骤: - 创建XMLHttpRequest对象。 - 定义请求类型、URL和是否异步。 - 发送请求并接收响应。 - 处理响应数据并更新页面内容。 6. 常见的Ajax框架和库: - jQuery:一个流行的JavaScript库,提供了简化Ajax开发的方法和函数。 - Axios:一个基于Promise的HTTP客户端,用于浏览器和Node.js。 - Fetch API:一种用于发送和接收网络请求的新标准。 这只是Ajax学习的一些基本概念和步骤,您可以进一步深入学习Ajax的相关知识和实践。希望这些笔记对您有所帮助!如有更多问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值