AJAX 入门

框架前置课

7740ff98b4d7433eab8fa0bf221124e0.png


d8eac84068944f1686ece10fe2295f10.png


学完收获:前后端数据交互技术,前端工程化、模块化、npm包管理器,Git代码版本控制系统


AJAX入门

让数据活起来

一、什么是 AJAX

定义异步 JavaScript 和 XML,或 Ajax 本身不是一种技术,而是一种将一些现有技术结合起来使用的方法,包括:HTMLXHTMLCSSJavaScriptDOMXMLXSLT、以及最重要的 XMLHttpRequest 对象。当使用结合了这些技术的 Ajax 模型以后,网页应用能够快速地将增量更新呈现在用户界面上,而不需要重载(刷新)整个页面。这使得程序能够更快地回应用户的操作。Ajax 最吸引人的特性是它的“异步”性质,这意味着它可以与服务器通信、交换数据并更新页面,而无需刷新页面。

简单点说,就是使用 XMLHttpRequest 对象与服务器通信。


概念:AJAX 是浏览器与服务器进行数据通信的技术

f1b7078c3b034b37b42a37537daabb7e.png


二、怎么用 AJAX?

先使用 库,与服务器进行数据通信

  • 基于 XMLHttpRequest 封装、代码简单、月下载量在 14 亿次

  • Vue、React 项目中都会用到 axios

再学习 XMLHttpRequest 对象的使用,了解 AJAX 底层原理


三、axios 使用

语法:

引入:axios.js:https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js

使用 axios 函数

  • 传入配置对象

  • 再用.then回调函数接收结果,并做后续处理

 axios({
     url: ' http://hmajax.itheima.net/api/province'
     }).then(result => {
         console.log(result);
 })

需求:请求目标资源地址,拿到省份列表数据,显示到页面

目标资源地址: http://hmajax.itheima.net/api/province

bca9a64d15b94f60a13c0b865e38d16e.png


 <p class="my-p"></p>
 <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
 <script>
     axios({
     url: ' http://hmajax.itheima.net/api/province'
     }).then(result => {
         document.querySelector('.my-p').innerHTML = result.data.list.join('<br>')
 })    
 </script>

认识 URL

一、什么是URL?

原因:知道作用和组成,方便与后端人员沟通

概念:URL 就是统一资源定位符,简称网址,用于访问网络上的资源

二、URL的组成

组成:

b6f6b43c12aa4c7083837038ae2db30f.png


协议:

342c9f03f3324b09a8a0381639db34fe.png


域名:

a3750b1f4e5049f8abd77d31805cac24.png


资源路径:

2f080975c7f1467a84745b54dd29b120.png


三、URL 查询参数

定义:浏览器 提供给服务器的额外信息,让服务器返回浏览器想要的数据。

语法: http://xx.comxxx/xxx ?参数名1=值1 & 参数名2=值2

f0a2af9b771841efb1f285b9c8e383cf.png


语法:使用 axios 提供的 params选项

 axios({
   url: '目标资源地址'
   params: {
     参数名: 值
   }
 }).then(result =>{
     // 对服务器返回的数据做后续处理
 })

城市列表:http://hmajax.itheima.net/api/city?pname=河北省

 <p class="my-p"></p>
 <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 => {
         document.querySelector('.my-p').innerHTML = result.data.list.join('<br>')
     })    
 </script>

常用请求方法

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

854af1194b3c464896f08cd1ee27e40b.png


一、数据提交

场景:当数据需要在服务器上保存

74a8b718b29440aeabe19a74d67c1924.png


二、axios 请求配置

 url: 请求的 URL 网址
 method: 请求的方法,==GET==可以省略 (不区分大小写)
 data: 提交数据

语法:

 axios({
   url: '目标资源地址',
   method: '请求方法',
   data: {
     参数名: 值
  }
 }).then(result =>{
     // 对服务器返回的数据做后续处理
 })

三、数据提交

需求:通过 axios 提交用户名和密码,完成注册功能

注册用户 URL地址:http://hmajax.itheima.net/api/register

请求方式: POST

参数名:

username 用户名 (中英文和数字组成,最少8位)

password 密码 (最少6位)

59dd53484d4e4640be509d1381e22662.png


 <button class="btn">注册用户</button>
 <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
 <script>
     document.querySelector('.btn').addEventListener('click',() =>{
       axios({
         url: 'http://hmajax.itheima.net/api/register',
         method: 'post',
         data: {
           username: 'itheima123',
           password: '36974213'
        }
      }).then(result => {
       console.log(result);
     })
   })
     
 </script>

四、axios 的核心配置

8a4b5b8f846c45df93e2c9f8ae95beee.png


axios 错误处理

场景:再次注册相同的账户,会遇到报错信息

a2a4354a927c4cd0aac2c3e13a7975f7.png


5639e36536e647ea97e89ed43c9edf14.png


处理:用更直观的方式,给普通用户展示错误信息

a161d179a6414564834fe89089260af3.png


语法:在then方法的后面,通过点语法调用 catch方法,传入回调函数并定义形参

 axios({
   // 请求选项
 }).then(result =>{
   // 处理数据
 }).catch(error =>{
   // 处理错误
 })

 <button class="btn">注册用户</button>
 <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
 <script>
 document.querySelector('.btn').addEventListener('click',() =>{
   axios({
     url: 'http://hmajax.itheima.net/api/register',
     method: 'post',
     data: {
       username: 'itheima123',
       password: '36974213'
    }
   }).then(result => {
    // 成功就调用 then方法
      console.log(result);
   }).catch(error => {
    // 失败就调用 catch方法
    // 处理错误信息
      alert(error.response.data.message)
     })
  })
     
 </script>

HTTP协议 - 请求报文

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

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

c9130cf4cd76491fa86c4e55285644c8.png


一、请求报文的格式

请求报文的组成部分有:

  1. 请求行:请求方法,URL,协议

  2. 请求头: 以键值对的格式携带的附加信息,比如:Content-Type

  3. 空行:分割请求头,空行之后的是发送给服务器的资源

  4. 请求体:发送资源

4749c279c72d4935b4021f5e809085cc.png


二、请求报文 - 错误排查

需求:通过请求报文排查错误原因,并修复

d721413d37504a22820a48ac2a5acdf9.png


8d486e92cf3a40adba118194545e737a.png


 <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
   <script>
     // 1.获取 alert
     const alertCom = document.querySelector('.alert')
 ​
     // 2.抽取提示框的方法
     function showAlert(msg, classname) {
       alertCom.innerText = msg
       alertCom.classList.add(classname)
       alertCom.classList.add('show')
       setTimeout(() => {
         // 延迟隐藏
         alertCom.classList.remove('show')
         alertCom.classList.remove(classname)
       }, 2000);
     }
 ​
     // 3.给登录按钮绑定点击事件,提交输入的用户信息到服务器
     document.querySelector('.btn-login').addEventListener('click', function () {
       // 3.1 获取输入的用户名和密码
       const username = document.querySelector('.username').value
       // 类名错误
       const password = document.querySelector('.username').value
 ​
       // 3.2用户名 密码 长度判断
       if (username.trim().length < 8) {
         showAlert('用户名长度需要大于等于8', 'alert-danger')
         return
       }
       if (password.trim().length < 6) {
         showAlert('密码长度需要大于等于6', 'alert-danger')
         return
       }
 ​
       // 3.3 通过axios提交到服务器 并 提示用户 成功 / 失败
       axios({
         url: 'http://hmajax.itheima.net/api/login',
         method: 'post',
         data: {
           username,
           password
         }
       }).then(res => {
         // 显示提示框
         showAlert(res.data.message, 'alert-success')
       }).catch(err => {
         // 显示警示框
         showAlert(err.response.data.message, 'alert-danger')
       })
     })
   </script>

HTTP协议 - 响应报文

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

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

4c199d91182140cc89c0dbc9833c7ef4.png


一、HTTP响应状态码

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

比如:404 (服务器找不到资源)

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

5049f080ffa84d4b824a201cc5e0b8c8.png


接口文档

接口文档:描述接口的文章

接口:使用 AJAX 和服务器通讯时,使用 URL,请求方法,以及参数

axios({
  url: 'http://hmajax.itheima.net/api/city',
  method: 'get',
  params: {
	pname: '辽宁省'
  }
})

54b8c039afa84c84a8083505346abc0c.png


传送门:AJAX阶段接口文档


登录案例

e94522fd542a42e884519ebae21dddc6.png


974d9a41fba740fca2c45bc1e189e0f3.png


  1. 点击登录时,判断用户名和密码长度

  2. 提交数据和服务器通信

  3. 提示信息

 <!-- 引入bootstrap.css -->
   <link rel="stylesheet" href="https://cdn.jsdelivr.net/
      npm/bootstrap@5.2.2/dist/css/bootstrap.min.css">
   <!-- 公共 -->
   <style>
     html,
     body {
       background-color: #EDF0F5;
       width: 100%;
       height: 100%;
       display: flex;
       justify-content: center;
       align-items: center;
     }
 ​
     .container {
       width: 520px;
       height: 540px;
       background-color: #fff;
       padding: 60px;
       box-sizing: border-box;
     }
 ​
     .container h3 {
       font-weight: 900;
     }
   </style>
   <!-- 表单容器和内容 -->
   <style>
     .form_wrap {
       color: #8B929D !important;
     }
 ​
     .form-text {
       color: #8B929D !important;
     }
   </style>
   <!-- 提示框样式 -->
   <style>
     .alert {
       transition: .5s;
       opacity: 0;
     }
 ​
     .alert.show {
       opacity: 1;
     }
   </style>

 <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.获取提示框
     const myAlert = document.querySelector('.alert')
     // 功能:
     /**
     *   1.显示提示框
     *   2.不同提示文字msg,成功绿色失败红色 isSuccess (true 成功,false 失败)
     */
     // 2.封装提示框函数,重复调用,满足提示需求
     function alertFn(msg,isSuccess) {
         // 1.显示提示框
         myAlert.classList.add('show')
         
         // 2. 实现细节
         myAlert.innerText = msg
         const bgStyle = inSuccess ? 'alert-success' : 'alert-dange'
         myAlert.classList.add(bgStyle)
         
         // 3. 过2秒隐藏
         setTimeout(()=> {
             myAlert.classList.remove('show')
             // 避免类名冲突,重置背景色
             myAlert.classList.remove(bgStyle)
         },2000)
     }
     
     document.querySelector('.btn-login').addEventListener('click', () => {
         // 获取用户名和密码
         const username = document.querySelector('.username').value
         const password = document.querySelector('.password').value
         
         // 判断长度
         if (username.length < 8) {
             alertFn('用户名必须大于等于8位',false)
             console.log('用户名必须大于等于8位')
             return  // 阻止代码继续执行
         }
         if (password.length < 6) {
             alertFn('密码必须大于等于6位',false)
             console.log('密码必须大于等于6位')
             return  // 阻止代码继续执行
         }
         // 基于 axios 提交用户名和密码
         axios({
             url: 'http://hmajax.itheima.net/api/login',
             method: 'POST',
             data: {
                 username,
                 password
             }
             }).then(result => {
             alertFn(result.data.message,true)
           console.log(result.data.message)
         }).catch(error => {
             alertFn(error.response.data.message,false)
             console.log(error.response.data.message)
         })
     })
 </script>

Form-serialize插件

作用:快速收集表单元素的值

bd9d8ed8252c48599bb2bb8bf13a4f17.png


语法:

const form = document.querySelector('.example-form')

const data = serialize(form, { hash: true,empty: true })

a749f17772954b78b2e7d3ffa57532ac.png


<form action="javascript:;" class="examp-form">
    <input type="text" name="uname">
    <br>
    <input type="text" name="pwd">
    <br>
    <input type="button" class="btn" value="提交">
</form>
<script src="./lib/form-serialize.js"></script>
<script>
    document.querySelector('.btn').addEventListener('click', () => {
        const form = document.querySelector('.examp-form')
        const data = serialize(form,{hash: true,empty: true})
        console.log(data);
    })
</script>

33e46b7bd41145f8ac7533d038fa51c1.png


Bootstrap 弹框

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

步骤:

  1. 引入 bootstrap.css 和 bootstrap.js

  2. 准备弹框标签,确认结构

  3. 通过自定义属性,控制弹框的显示和隐藏


 <button data-bs-toggle="modal" data-bs-target="css选择器">
    显示弹框
</button>
<button data-bs-dismiss="modal">Close</button>

d28ecd64886f44ceb74b319b1a795de1.png


<link href="https://cdn.jsdelivr.net/npm/bootstrap
    @5.2.2/dist/css/bootstrap.min.css" rel="stylesheet">
    <title>Document</title>
</head>

<body>
    <!-- 
        目标:使用Bootstrap 弹框
        1. 引入bootstrap.css 和 bootstrap.js
        2. 准备弹框标签,确认结构
        3. 通过自定义属性,控制弹框的显示和隐藏
     -->
     <button type="button" class="btn btn-primary" data-bs-toggle="modal" 
       data-bs-target=".my-box">
        显示弹框
     </button>

     <!-- 
        弹框标签
        bootstrap的modal弹框:添加 modal类名 (默认隐藏)
      -->
    <div class="modal my-box" tabindex="-1">
        <div class="modal-dialog">
            <!-- 弹框-内容 -->
            <div class="modal-content">
                <!-- 弹框-头部 -->
                <div class="modal-header">
                    <h5 class="modal-title">Modal title</h5>
                    <button type="button" class="btn-close" 
                     data-bs-dismiss="modal" aria-label="Close"></button>
                </div>
                <!-- 弹框-主体 -->
                <div class="modal-body">
                    <p>Modal body text goes here.</p>
                </div>
                <!-- 弹框 - 底部 -->
                <div class="modal-footer">
                    <button type="button" class="btn btn-secondary" 
                      data-bs-dismiss="modal">Close</button>
                    <button type="button" class="btn btn-primary">
                        Save changes</button>
                </div>
            </div>
        </div>
    </div>

     <!-- 引入bootstrap.js -->
     <script src="https://cdn.jsdelivr.net/npm/bootstrap
         @5.2.2/dist/js/bootstrap.min.js"></script>

  1. 通过 JS 控制,弹框显示或隐藏

09d3c14fec614c269478a88abba00c3c.png


 // 创建弹框对象
 const modalDom = document.querySelector('css选择器')
 const modal = new bootstrap.Modal(modalDom)
 ​
 // 显示弹框
 modal.show()
 // 隐藏弹框
 modal.hide()

 <!-- 
     目标:使用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>
         // 1. 创建弹框对象
         const modalDom = document.querySelector('.name-box')
         const modal = new bootstrap.Modal(modalDom)
 ​
         // 编辑姓名->点击->赋予默认姓名->弹框显示
         document.querySelector('.edit-btn').addEventListener('click', () => {
             document.querySelector('.username').value = '默认姓名'
 ​
             // 2. 显示弹框
             modal.show()
         })
 ​
         // 保存->点击->->获取姓名打印->弹框隐藏
         document.querySelector('.save-btn').addEventListener('click', () => {
             const username = document.querySelector('.username').value
             console.log('模拟把姓名保存到服务器上', username)
 ​
             // 2. 隐藏弹框
             modal.hide()
         })
     </script>

图片上传

bde5a24d93b3416baed5d2ceae828ff1.png


获取图片文件对象

231914276ecf4c3b8636fbdc44df0592.png


使用 FormData 携带图片文件

const fd = new FormData()
  fd.append(参数名, 值)

 

f4010da6faf841a483733e8821397543.png


提交表单数据到服务器,使用图片 url 网址

<!-- 文件选择元素 -->
<input type="file" class="upload">
<img src="" alt="" class="my-img">
 ​
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
 ​
<script>
 /**
  * 目标:图片上传,显示到网页上
  * 1. 获取图片文件
  * 2. 使用 FormData 携带图片文件
  * 3. 提交到服务器,获取图片 url 网址使用
 */
  // 文件选择元素 -> change改变事件
  document.querySelector('.upload').addEventListener('change', e => {
  // 1.获取图片文件
    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>

AJAX 原理 - XHR

定义: XMLHTTPRequest(XHR) 对象 用于与服务器交互。通过XMLHttpRequest 可以在不刷新页面的情况下请求特定 URL,获取数据。这允许网页在不影响用户操作的情况下,更新页面的局部内容。XMLHttpRequest 在AJAX编程中被大量使用。

关系:axios 内部采用 XMLHttpRequest 与服务器交互·

20628611385a49deb9a00ffe5e1f3aac.png


好处:掌握使用 XHR 与服务器进行数据交互,了解 axios 内部原理

为什么学习 XHR?

  • 有更多与服务器数据通信方式

  • 了解 axios 内部原理


一、使用 XHR

步骤:

  1. 创建 XMLHttpRequest对象

  2. 配置请求方法和请求 url

  3. 监听 loadend 事件,接收响应结果

  4. 发送请求

 const xhr = new XMLHttpRequest()
 xhr.open('请求方法','请求url网址')
 xhr.addEventListener('loadend', () => {
     // 响应结果
     console.log(xhr.response)
 })
 xhr.send()

e090029ab6c34b908f98adeda52bae08.png


需求:获取并展开所有省份名字

目标资源网址:http://hmajax.itheima.net/api/province

<script>
	// 1.创建 XMLHttpRequest对象
	const xhr = new XMLHttpRequest()
	// 2.配置请求方法和请求 url 地址
	xhr.open('GET','http://hmajax.itheima.net/api/province')
	// 3.监听 loadend 事件,接收响应结果
	xhr.addEventListener('loadend', () => {
    // 对响应结果做后续处理
  	 console.log(xhr.response)	// JOSN字符串
  	 // 将 JSON字符串转成对象
  	  const data = JSON.parse(xhr.response)
   	 console.log(data.list.join('<br>'))
    document.querySelector('.my-p').innerHTML = data.list.join('<br>')
})
4. 发送请求
xhr.send()   
 
</script>

二、XHR - 查询参数

定义:浏览器提供给服务器的额外信息,让服务器返回浏览器想要的数据

语法:http://xxx.com/xxx/xxx ?参数名1 &参数名2=值2

98bc653976bb446a9e82db9429204a1c.png


<p class="city-p"></p>
<script>
  const xhr = new XMLHttpRequest()
  xhr.open('GET', 'http://hmajax.itheima.net/api/province?pname=辽宁省')
  xhr.addEventListener('loadend', () => {
    // 响应结果
   const data = JSON.parse(xhr.response)
      document.querySelector('.city-p').innerHTML = data.list.join('<br>')

  })
   xhr.send()
</script>

三、地区查询案例

需求:输入省份和城市名字,查询地区列表

请求地址:http://hmajax.itheima.net/api/province ? 参数名=值1 & 参数名2=值2

// 1.创建 URLSearchParams 对象
const paramsObj = new URLSearchParams({
	参数名1: 值1,
	参数名2: 值2
})

// 2. 生成指定格式查询参数 字符串
const queryString = paramsObj.toString()
// 结果: 参数名1= 值1 & 参数名2 = 值2

<script>
	// 1. 查询按钮-点击事件
	document.querySelector('.sel-btn').addEventListener('click',() => {
	// 2. 收集省份和城市名字
	const pname = document.querySelector('.province').value
	const cname = document.querySelector('.city').value
	// 3. 组织查询参数字符串
	const qObj = {
		pname,
		cname
	}
	// 查询参数对象 -> 查询参数字符串
	const paramsObj = new URLSearchParams(qObj)
	const queryString = paramsObj.toString()
})
	// 4.使用XHR对象,查询地区列表
	const xhr = new XMLHttpRequest()
      xhr.open('GET', `http://hmajax.itheima.net/api/area?${queryString}`)
      xhr.addEventListener('loadend', () => {
     // 响应结果
      const data = JSON.parse(xhr.response)
      const htmlStr = data.list.map(areaName => {
         return `<li class="list-group-item">${areaName}</li>`
     }).join('')
      document.querySelector('.list-group').innerHTML = htmlStr
   })
    xhr.send()
</script>

四、数据提交

需求:通过 XHR 提交用户名和密码,完成注册功能

核心:

请求头设置 content-Type: application/json

请求体携带 JSON 字符串

const xhr = new XMLHttpRequest()
xhr.open('请求方法','请求url网址')
xhr.addEventListener('loadend', () => {
    console.log(xhr.response)
})
// 告诉服务器,我传递的内容类型,是JSON字符串
xhr.setRequerstHeader('content-Type','application/json')
 // 准备数据并转成 JSON字符串
 const user = { username: 'itheima007', password: '7654321' }
 const userStr = JSON.stringify(user)
// 发送请求体数据
xhr.send(userStr)

<button class="reg-btn">注册用户</button>
<script>
/**
* 目标:使用xhr进行数据提交-完成注册功能
*/
  document.querySelector('.reg-btn').addEventListener('click',()=>{
    
})
	

<button class="reg-btn">注册用户</button>
<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.setRequerstHeader('content-Type', 'application/json')
     // 准备数据并转成 JSON字符串
     const user = { 
       username: 'itheima007', 
        password: '7654321'
    }
     const userStr = JSON.stringify(user)
   // 发送请求体数据
    xhr.send(userStr)
  })
    </script>

Promise

一、Promise 的使用

定义:

promise 对象用于表示一个异步操作的最终完成 (或失败) 及其结果值。

好处:

  1. 逻辑更清晰

  2. 了解 axios 函数内部运行机制

  3. 能解决回调函数地狱问题

语法:

// 1.创建Promise对象
const p = new Promise({resolve,reject}=>{
	// 2.执行异步任务-并传递结果
	// 成功调用:resolve(值)触发 then()执行
	// 失败调用: reject(值) 触发catch() 执行
})
// 3. 接收结果
p.then(result=>{
	// 成功
}).catch(error=>{
	// 失败
})

c40cb7b7f8844d8b80cdd429a5095727.png


 <script>
   const p = new Promise((resolve, reject) => {
     setTimeout(() => {
   // resolve('模拟AJAX请求-成功结果')
   reject(new Error('模拟AJAX请求-失败结果'))
     }, 2000)
  })
    p.then(result => {
     console.log(result);
  }).catch(error => {
     console.log(error);
  })
 </script>

总结:

什么是 Promise?

  • 表示 (管理) 一个异步操作最终状态和结果值的对象

为什么学习 Promise?

  • 成功和失败状态,可以关联对应处理程序

  • 了解 axios 内部原理


二、Promise - 三种状态

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

24d7544df1e2466a8180865c693f925f.png


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

  • 待定 ( pending ) : 初始状态,既没有被兑现,也没有被拒绝

4e8cf764153c43f2ab096dd3ec821084.png


  • 已兑现 ( fulfilled ) : 意味着,操作成功完成

  • 已拒绝 ( rejected ) : 意味着,操作失败


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


<script>
    // 1.创建Promise对象 (pending-待定状态)
    const p = new Promise((resolve, reject) => {
        // Promise对象创建时,这里的代码会立即执行
        setTimeout(() => {
            // resolve() => 'fulfilled状态 - 已兑现' => 调用 then()
            // resolve('模拟AJAX请求-成功结果')
            // reject() => 'rejected状态 - 已拒绝' => 调用 catch()
            // reject(new Error('模拟AJAX请求-失败结果'))
        }, 2000)
    })
    console.log(p);     // pending状态
    p.then(result => {
        console.log(result);
    }).catch(error => {
        console.log(error);
    })
</script>

总结: Promise 状态有什么用?

  • 状态改变后,调用关联的处理函数


三、使用 Promise + XHR 获取省份列表

053e8871335f427697324e6aaed131a5.png


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

步骤:

  1. 创建 Promise 兑现

  2. 执行 XHR 异步代码,获取省份列表

  3. 关联成功或失败函数,做后续处理


<div class="my-p"></div>
<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开头的都是成功响应状态码
    if (xhr.status >= 200 && xhr.status < 300) {
       resolve(JSON.parse(xhr.response))
    } else {
       reject(new Error(xhr.response))
   }
 })
     xhr.send()
 })

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

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


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

步骤:

  1. 定义 myAxios 函数,接收配置对象,返回Promise对象

  2. 发起 XHR请求,默认请求方法为 GET

  3. 调用成功/失败的处理程序

function myAxios(config) {
  return new Promise((resolve,reject) => {
  // XHR请求
  // 调用成功/失败的处理程序
  })
}
myAxios({
  url: '目标资源地址'
}).then(result => {
	
}).catch(error => {

})

<script>
  // 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()
   })
  }
  myAxios({
     url: 'http://hmajax.itheima.net/api/province'
  }).then(result => {
     console.log(result)
  }).catch(error => {
     onsole.log(error);
  })
</script>

五、封装_简易 axios _获取地区列表

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

要求:

  • myAxios 函数调用后,传入 params选项

function myAxios(config) {
  return new Promise((resolve,reject) => {
  // XHR请求 - 判断 params选项,携带查询参数
  // 调用成功/失败的处理程序
  })
}

myAxios({
  url: '目标资源地址',
  params: {
	参数名1: 值1,
	参数名2: 值2
  }
})

  1. 基于 URLSearchParams 转换查询参数字符串

  2. 使用自己封装的 myAxios 函数展示地区列表

<script>
function myAxios(config) {
  return new Promise((resolve,reject) => 
{
	const xhr = new XMLHttpRequest()
	// 1.判断有 params 选项,携带查询参数
	if(config.params) {
    // 2. 使用 URLSearchParams 转换,并携带到 url 上
	const paramsObj = new URLSearchParams(config.params) 
	const queryString = paramsObj.toString()

    // 把查询参数字符串,拼接在url? 后面
	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))
  }
 })
    xhr.send()
  })
}
</script>

六、封装 _ 简易axios _ 注册用户

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

步骤:

  1. myAxios 函数调用后,判断 data选项

  2. 转换数据类型,在 send 方法中发送

  3. 使用 myAxios 函数,完成注册用户

<button class="reg-btn">注册用户</button>
<script>
function myAxios(config) {
  return new Promise((resolve,reject) => {
	const xhr = new XMLHttpRequest()
  // 1.判断有 params 选项,携带查询参数
  if(config.params) {
  // 2. 使用 URLSearchParams 转换,并携带到 url 上
    const paramsObj = new URLSearchParams(config.params) 
	const queryString = paramsObj.toString()
// 把查询参数字符串,拼接在url? 后面
	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))
       }
   })
    // 1.判断有data选择,携带请求体
    if(config.data) {
   // 2. 转换数据类型,在 send 中发送
     const jsonStr = JSON.stringify(config.data)
       xhr.setRequestHeader('Content-Type','application/json')
       xhr.send(jsonStr)
     }else {
    // 如果没有请求体参数,就正常的发起请求
      xhr.send()
    }
           
  })
}

myAxios({
  url: 'http://hmajax.itheima.net/api/register',
  method: 'POST',
  data: {
    username: 'itheima999',
    password: '666666'
  }
}).then(result => {
   console.log(result)
}).catch(error => {
  console.log(error)
})
</script>

同步代码与异步代码

同步代码:我们应该注意的是,实际上浏览器是按照我们书写代码的顺序一行一行地执行程序的。浏览器会等待代码的解析和工作,在上一行完成后才会执行下一行。这样做是很有必要的,因为每一行新的代码都是建立在前面代码的基础之上的。


这也使得它成为一个同步程序

同步代码:逐行执行,需原地等待结果后,才继续向下执行


异步代码:异步编程技术 使你的程序可以在执行一个可能长期运行的任务的同时继续对其他事件做出反应而不必等待任务完成。与此同时,你的程序也将在任务完成后显示结果。

异步代码:调用后耗时,不阻塞代码继续执行 (不必原地等待),在将来完成后触发一个回调函数


同步与异步

例子:回答打印数字的顺序是什么?

<script>
 const result = 0 + 1
	console.log(result)
  setTimeout(()=>{
    console.log(2)
  },2000)

 document.querySelector('.btn').addEventListener('click',()=>{
   console.log(3)
})

document.bodu.style.backgroundColor = 'pink'
  console.log(4)
</script>

打印结果:1,4,2

点击按钮一次就打印一次3

异步代码接收结果:使用回调函数


回调函数地狱

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

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

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


8a6d62fc124e46caa82992c7af992254.png


<script>
axios({
   url: 'http://hmajax.itheima.net/api/province'
}).then(result => {
    const pname = result.data.list[0]
    document.querySelector('.province').innerHTML = pname
    // 获取第一个省份默认下属的第一个城市名字
    axios({
      url: 'http://hmajax.itheima.net/api/city',
      params: {
         pname
     }
    }).then(result => {
     const cname = result.data.list[0]
      document.querySelector('.city').innerHTML = cname
     // 获取第一个城市默认下属的第一个地区名字
       axios({
         url: 'http://hmajax.itheima.net/api/area',
         params: {
            pname, cname
        }
      }).then(result => {
      document.querySelector('.area').innerHTML = result.data.list[0]
    })
   })
 })
</script>

Promise - 链式调用

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

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

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

093241ea41b5454389173fe4f7ef6fa8.png


<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)
   })
})

// 4. 获取城市名字
 p2.then(result => {
   console.log(result);
})
// 结果为 false 说明 then() 原地的结果是一个新的Promise对象
  console.log(p2 === p);      // false
</script>

Promise链式应用:

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

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

8cc0991c1b134c21aada7bb7ab8a7da2.png


<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
  // 1. 得到-获取省份 Promise 对象
  axios({
    url: 'http://hmajax.itheima.net/api/province'
 }).then(result => {
  const 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 => {
    const cname = result.data.list[0]
    document.querySelector('.city').innerHTML = cname
    // 3. 得到-获取地区Promise对象
    return axios({
     url: 'http://hmajax.itheima.net/api/area',
     params: {
       pame, cname
    }
  })
}).then(result => {
   const areaName = result.data.list[0]
   document.querySelector('.area').innerHTML = areaName
 })
</script>

async 函数和 await

定义:async 函数 是使用 async 关键字声明的函数。async 函数 是 AsyncFunction 构造函数 的实例,并且其中运行使用 await 关键字。async 和 await 关键字让我们可以用一种更简洁的方式写出基于 Promise 的异步行为,而无需刻意地链式调用 promise。

概念:在 async 函数内,使用 await 关键字取代 then 函数,等待获取 Promise 对象成功状态的结果值

示例:

<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
  // 获取默认省市区
  async function getDefaultArea() {
    const pObj = await axios({ url: 'http://hmajax.itheima.net/api/province' })
    const pname = pObj.data.list[0]
    const cObj = await axios({ url: 'http://hmajax.itheima.net/api/city',
      params: { pname } })
        const cname = cObj.data.list[0]
        const aObj = await axios({ url: 'http://hmajax.itheima.net/api/area', 
          params: { pname, cname } })

  const aname = aObj.data.list[0]
  // 赋予到页面上
  document.querySelector('.province').innerHTML = pname
  document.querySelector('.city').innerHTML = cname
  document.querySelector('.area').innerHTML = aname
 }
  getDefaultArea()
</script>

async 函数 和 await_捕获错误

使用:

58179b1a421a4039836b51d8c8b22718.png


语法:

try {

  // 要执行的代码

}catch (error) {

  // error 接收的是,错误信息
  // try 里代码,如果有错误直接进入这里执行

}

<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
/**
* 目标:async 和 await_错误捕获
*/
<script>
  // 获取默认省市区
  async function getDefaultArea() {
    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/area',
        params: { pname, cname } })
      const aname = aObj.data.list[0]

      // 赋予到页面上
      document.querySelector('.province').innerHTML = pname
      document.querySelector('.city').innerHTML = cname
      document.querySelector('.area').innerHTML = aname
    } catch (error) {

     // 2. 接着调用 catch块,接收错误信息
     // 如果 try 里某行代码报错后,try中剩余的代码就不会执行了
      console.dir(error)
    }
  }
    getDefaultArea()
</script>

事件循环 (EventLoop)

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

4c644db2a3264e7eae92bad352172f64.png


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

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


一、事件循环 - 执行过程

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

7f2f7a90be9c4e919b248d0958a74d0f.png


二、宏任务与微任务

ES6 之后引入了 Promise 对象,让 JS 引起也可以发起异步任务

异步任务分为:

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

  • 微任务: 由JS 引擎环境执行的异步代码

任务 (代码)执行所在环境
JS脚本执行事件 (script)浏览器
setTimeout/setInterval浏览器
AJAX请求完成事件浏览器
用户交互事件等浏览器

任务 (代码)执行所在环境
Promise对象.then()JS引擎

Promise 本身是同步的,而 then 和 catch 回调函数是异步的


三、宏任务与微任务 - 执行顺序

0cfd54de93d146ff89724483e2c3a674.png


先调度微任务,再调度宏任务。(优先清空微任务)


四、Promise.all 静态方法

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

1c36e17d160f42e2b8bbcb02af4f7930.png


语法:

const p = Promise.all([Promise对象,Promise对象,...])
p.then(result => {
	// result结果:[ Promise对象成功结果,Promise对象成功结果,...]
}).catch(error => {
	// 第一个失败的 Promise对象,抛出异常
})

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

<ul class="my-ul"></ul>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
    /**
     * code
     * 北京-110100
     * 上海-310100
     * 广州-440100
     * 深圳-440300
    */
    // 1.请求城市天气,得到Promise对象
    const bjPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', 
      params: { city: '110100' } })
    const shPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', 
      params: { city: '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 htmlStr = result.map(item => {
            return `<li>${item.data.data.area} --- ${item.data.data.weather}</li>`
        }).join()
        document.querySelector('.my-ul').innerHTML = htmlStr
    }).catch(error => {
        console.dir(error)
    })
</script>

 

  • 14
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值