Ajax入门以及Axios的详细使用(含Promise)

news/2025/1/9 4:26:23/文章来源:https://www.cnblogs.com/iRuriCatt/p/18636389

1. 概述

1.1 是什么

  • Ajax = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)

  • Ajax 不是新的编程语言,而是一种用于创建快速动态网页的技术

  • Ajax 最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容,使网页实现异步更新

  • 传统的网页(不使用 Ajax)如果需要更新内容,必需重载整个网页

  • Ajax 不需要任何浏览器插件,但需要用户允许 JavaScript 在浏览器上执行

  • XMLHttpRequest只是实现 Ajax 的一种方式

1.2 为什么

  • 以前数据都是写在代码里固定的, 无法随时变化

  • 现在数据可以从服务器上进行获取,让数据变活

1.3 入门程序

  • 需求:从服务器获取省份列表数据,展示到页面上

  • 步骤:

    • 引入 axios
    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
    
    • 基本语法
    axios({url: "目标资源地址",
    }).then(result => {// 对服务器返回的数据做后续处理
    });
    
  • 示例

<div id="root"></div>
<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("#root").innerHTML = result.data.list.join("<br>");});
</script>

2. axios

2.1 URL

  • URL:统一资源定位符,简称网址,用于定位网络中的资源(网页,图片,数据,视频,音频等)

  • 组成:协议,域名,资源路径(比较重要的三部分)

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

  • 域名:标记服务器在互联网当中的方位,网络中有很多服务器,你想访问哪一台,需要知道它的域名

  • 资源路径:一个服务器内有多个资源,用于标识你要访问的资源具体的位置

  • 查询参数:携带给服务器额外信息,让服务器返回想要的某一部分数据而不是全部数据

    • 格式:http://xxxx.com/xxx/xxx?参数名1=值1&参数名2=值2

    • 参数名一般是后端规定的,值前端看情况传递即可

  • axios 如何携带查询参数?

axios({url: "目标资源地址",params: {参数名: 值,},
}).then(result => {// 对服务器返回的数据做后续处理
});
  • 示例 1:获取“河北省”下属的城市列表
<div id="root"></div>
<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("#root").innerHTML = result.data.list.join("<br>");});
</script>
  • 示例 2:根据输入的省份名字和城市名字,查询下属地区列表

<!-- 样式 -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" />
<style>#root {font-size: 15px;}body {padding-top: 15px;}
</style><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: 城市名字*/// 绑定点击事件document.querySelector(".sel-btn").addEventListener("click", () => {// 获取输入框的值let pName = document.querySelector(".province").value;let cName = document.querySelector(".city").value;// 利用axios获取数据axios({url: "http://hmajax.itheima.net/api/area",params: {pname: pName,cname: cName,},}).then(result => {document.querySelector(".list-group").innerHTML = result.data.list.map(item => `<li class="list-group-item">${item}</li>`).join("");});});
</script>

2.2 数据提交

  • 常用请求方法
请求方法 操作
GET 获取数据(默认请求方式)
POST 提交数据
PUT 修改数据(全部)
DELETE 删除数据
PATCH 修改数据(部分)
  • axios 如何提交数据到服务器
axios({url: "目标资源地址",method: "请求方法",data: {参数名: 值,},
}).then(result => {// 对服务器返回的数据做后续处理
});
  • 示例:注册账号,提交用户名和密码到服务器保存
<button>点击注册</button>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>document.querySelector("button").addEventListener("click", () => {axios({url: "http://hmajax.itheima.net/api/register",method: "post",data: {username: "itheima666",password: "12345678",},}).then(result => {console.log(result);});});
</script>

2.3 axios 错误处理

axios({// ...请求选项
}).then(result => {// 处理成功数据}).catch(error => {// 处理失败错误});
  • 示例:
<button>点击注册</button>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>document.querySelector("button").addEventListener("click", () => {axios({url: "http://hmajax.itheima.net/api/register",method: "post",data: {username: "itheima666",password: "12345678",},}).then(result => {console.log(result);}).catch(error => {alert(error.response.data.message);});});
</script>

2.4 案例-用户登录

  • 样式
<!-- 引入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: 0.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:点击登录时,用户名和密码长度判断,并提交数据和服务器通信function alertFn(msg, isSuccess) {// 显示提示框let myAlert = document.querySelector(".alert");myAlert.classList.add("show");// 更换样式let bgc = isSuccess ? "alert-success" : "alert-danger";myAlert.classList.add(bgc);// 打印信息myAlert.innerText = msg;// 2s后自动消失setTimeout(() => {myAlert.classList.remove("show");// 重置背景色,避免类名冲突myAlert.classList.remove(bgc);}, 2000);}document.querySelector(".btn-login").addEventListener("click", () => {let username = document.querySelector(".username").value;let password = document.querySelector(".password").value;if (username.length < 8) {// console.log('用户名不能少于8个字符')alertFn("用户名不能少于8个字符", false);return;}if (password < 6) {// console.log('密码不能少于6个字符')alertFn("密码不能少于6个字符", false);return;}axios({url: "http://hmajax.itheima.net/api/login",method: "post",data: {username,password,},}).then(result => {// alert(result.data.message)alertFn(result.data.message, true);}).catch(error => {// alert(error.response.data.message)alertFn(error.response.data.message, false);});});
</script>

2.5 form-serialize 插件

快速收集目标表单范围内表单元素的值

  • 引入 form-serialize 插件

  • 使用 serialize 函数

    • 参数 1:要获取的 form 表单标签对象(要求表单元素有 name 属性,用来作为收集的数据中属性名)

    • 参数 2:配置对象

      • hash:

        • true - 收集出来的是一个 JS 对象

        • false - 收集出来的是一个查询字符串

      • empty:

        • true - 收集空值

        • false - 不收集空值

  • 示例:收集登录表单里用户名和密码

<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="./form-serialize.js"></script>
<script>document.querySelector(".btn").addEventListener("click", () => {const form = document.querySelector(".example-form");const data = serialize(form, { hash: true, empty: true });console.log(data);});
</script>

2.6 Bootstrap 弹框

2.6.1 属性控制

  • 引入 bootstrap.css 和 bootstrap.js

  • 准备弹框标签,确认结构(可以从 Bootstrap 官方文档的 Modal 里复制基础例子)- 运行到网页后,逐一对应标签和弹框每个部分对应关系

  • 通过自定义属性,通知弹框的显示和隐藏,语法如下:

<button data-bs-toggle="modal" data-bs-target="css选择器">显示弹框</button><button data-bs-dismiss="modal">Close</button>
  • 代码实现
<!-- 
目标:使用Bootstrap弹框
1. 引入bootstrap.css和bootstrap.js
2. 准备弹框标签,确认结构
3. 通过自定义属性,控制弹框的显示和隐藏
-->
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target=".mybox">显示弹框</button><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><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>

2.6.2 js 控制

  • 为什么需要 js 方式控制?

    • 当显示/隐藏之前,需要执行一些 JS 逻辑代码,就需要引入 JS 控制弹框显示/隐藏的方式
  • 例如:

    • 点击编辑姓名按钮,在弹框显示之前,在输入框填入默认姓名

    • 点击保存按钮,在弹框隐藏之前,获取用户填入的名字并打印

  • 语法
// 创建弹框对象
const modalDom = document.querySelector("css选择器");
const modal = new bootstrap.Modal(modelDom);// 显示弹框
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="./bootstrap.min.js"></script>
<script>const modalBox = document.querySelector(".name-box");const modal = new bootstrap.Modal(modalBox);document.querySelector(".edit-btn").addEventListener("click", () => {document.querySelector(".username").value = "默认姓名";modal.show();});document.querySelector(".save-btn").addEventListener("click", () => {const username = document.querySelector(".username").value;console.log("将数据提交到服务器", username);modal.hide();});
</script>

2.7 案例-图书管理

  • 黑马接口文档

  • 结构

<!-- 主体区域 -->
<div class="container"><!-- 头部标题和添加按钮 --><div class="top"><h3>图书管理</h3><button type="button" class="btn btn-primary plus-btn" data-bs-toggle="modal" data-bs-target=".add-modal">+ 添加</button></div><!-- 数据列表 --><table class="table"><thead class="table-light"><tr><th style="width: 150px;">序号</th><th>书名</th><th>作者</th><th>出版社</th><th style="width: 180px;">操作</th></tr></thead><tbody class="list"><!-- <tr><td>1</td><td>JavaScript程序设计</td><td>马特·弗里斯比</td><td>人民邮电出版社</td><td><span class="del">删除</span><span class="edit">编辑</span></td></tr> --></tbody></table>
</div>
<!-- 新增-弹出框 -->
<div class="modal fade add-modal"><!-- 中间白色区域 --><div class="modal-dialog"><div class="modal-content"><div class="modal-header top"><span>添加图书</span><button type="button" class="btn-close" aria-label="Close" data-bs-dismiss="modal"></button></div><div class="modal-body form-wrap"><!-- 新增表单 --><form class="add-form"><div class="mb-3"><label for="bookname" class="form-label">书名</label><input type="text" class="form-control bookname" placeholder="请输入书籍名称" name="bookname" /></div><div class="mb-3"><label for="author" class="form-label">作者</label><input type="text" class="form-control author" placeholder="请输入作者名称" name="author" /></div><div class="mb-3"><label for="publisher" class="form-label">出版社</label><input type="text" class="form-control publisher" placeholder="请输入出版社名称" name="publisher" /></div></form></div><div class="modal-footer btn-group"><button type="button" class="btn btn-primary" data-bs-dismiss="modal">取消</button><button type="button" class="btn btn-primary add-btn">保存</button></div></div></div>
</div>
<!-- 编辑-弹出框 -->
<div class="modal fade edit-modal"><!-- 中间白色区域 --><div class="modal-dialog"><div class="modal-content"><div class="modal-header top"><span>编辑图书</span><button type="button" class="btn-close" aria-label="Close" data-bs-dismiss="modal"></button></div><div class="modal-body form-wrap"><!-- 编辑表单 --><form class="edit-form"><!-- 保存正在编辑的图书id,隐藏起来:无需让用户修改 --><input type="hidden" class="id" name="id" /><div class="mb-3"><label for="bookname" class="form-label">书名</label><input type="text" class="form-control bookname" placeholder="请输入书籍名称" name="bookname" /></div><div class="mb-3"><label for="author" class="form-label">作者</label><input type="text" class="form-control author" placeholder="请输入作者名称" name="author" /></div><div class="mb-3"><label for="publisher" class="form-label">出版社</label><input type="text" class="form-control publisher" placeholder="请输入出版社名称" name="publisher" /></div></form></div><div class="modal-footer btn-group"><button type="button" class="btn btn-primary" data-bs-dismiss="modal">取消</button><button type="button" class="btn btn-primary edit-btn">修改</button></div></div></div>
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/1.2.0/axios.min.js"></script>
<script src="./lib/form-serialize.js"></script>
<script src="./lib/bootstrap.min.js"></script>
<!-- 核心逻辑 -->
<script src="./js/index.js"></script>
  • index.js

  • 渲染图书列表

/*** 目标1:渲染图书列表*  1.1 获取数据*  1.2 渲染数据*/const creator = "老李";function getBooksList() {// 1.1 获取数据axios({url: "http://hmajax.itheima.net/api/books",params: {creator,},}).then(result => {// console.log(result.data.data)// 1.2 渲染数据const list = result.data.data.map((item, index) => {return `<tr><td>${index + 1}</td><td>${item.bookname}</td><td>${item.author}</td><td>${item.publisher}</td><td data-id=${item.id}><span class="del">删除</span><span class="edit">编辑</span></td></tr>`;}).join("");document.querySelector(".list").innerHTML = list;});
}getBooksList();
  • 新增图书
/*** 目标2:新增图书*  2.1新增弹框->显示和隐藏*  2.2收集表单数据,并提交到服务器保存*  2.3刷新图书列表*/
const addModalDom = document.querySelector(".add-modal");
const addModal = new bootstrap.Modal(addModalDom);// 2.1 新增弹框->显示和隐藏
document.querySelector(".add-btn").addEventListener("click", () => {// 2.2 获取输入框的数据const addForm = document.querySelector(".add-form");const formData = serialize(addForm, { hash: true, empty: true });console.log(formData);// 2.3 提交到服务器保存axios({url: "http://hmajax.itheima.net/api/books",method: "post",data: {...formData,creator,},}).then(result => {console.log(result);// 2.4 重新渲染页面getBooksList();// 重置表单addForm.reset();// 点击保存,隐藏模态框addModal.hide();});
});
  • 删除图书
/*** 目标3:删除图书*  3.1删除元素绑定点击事件->获取图书id*  3.2调用删除接口*  3.3刷新图书列表*/
// 3.1 删除元素绑定点击事件(事件委托)->获取图书id
document.querySelector(".list").addEventListener("click", e => {if (e.target.classList.contains("del")) {const theId = e.target.parentNode.dataset.id;// 3.2 调用删除接口axios({url: `http://hmajax.itheima.net/api/books/${theId}`,method: "delete",}).then(() => {// 3.3 刷新图书列表getBooksList();});}
});
  • 编辑图书
/*** 目标4:编辑图书*  4.1编辑弹框->显示和隐藏*  4.2获取当前编辑图书数据->回显到编辑表单中*  4.3提交保存修改,并刷新列表*/
const editModalDom = document.querySelector(".edit-modal");
const editModal = new bootstrap.Modal(editModalDom);document.querySelector(".list").addEventListener("click", e => {if (e.target.classList.contains("edit")) {const theId = e.target.parentNode.dataset.id;// 4.2 获取当前编辑图书数据->回显到编辑表单中axios({url: `http://hmajax.itheima.net/api/books/${theId}`,method: "get",}).then(result => {// 数据对象"属性"和标签"类名"一致// 遍历数据对象,使用属性去获取对应的标签,快速赋值const bookObj = result.data.data;// console.log(bookObj)const keys = Object.keys(bookObj);keys.forEach(key => {document.querySelector(`.edit-form .${key}`).value = bookObj[key];});});// 4.1 编辑弹框->显示和隐藏editModal.show();}
});document.querySelector(".edit-btn").addEventListener("click", () => {const editForm = document.querySelector(".edit-form");const editData = serialize(editForm, { hash: true, empty: true });// 4.3 提交保存修改,并刷新列表axios({url: `http://hmajax.itheima.net/api/books/${editData.id}`,method: "put",data: {...editData,creator,},}).then(() => {getBooksList();});editModal.hide();
});
  • 总结

    • 渲染列表(查)

      • 获取数据

      • 渲染数据

    • 新增图书(增)

      • 弹框(显示/隐藏)

      • 收集数据&提交保存

      • 刷新页面列表

    • 删除图书(删)

      • 绑定点击事件(获取要删除的图书 id)

      • 调用删除接口(让服务器删除此数据)

      • 成功后重新获取并刷新列表

    • 编辑图书(改)

      • 弹框(显示/隐藏)

      • 表单(数据回显)

      • 点击修改收集数据,提交到服务器保存

      • 重新获取并刷新列表

2.8 图片上传

把本地的图片上传到网页上显示

  • 实现步骤

    • 获取图片文件对象:e.target.files[0]

    • 使用 FormData 表单数据对象装入

      const fd = new FormData();
      fd.append(参数名, 值);
      
    • 提交表单数据对象,使用服务器返回图片 url 网址

<!-- 文件选择元素 -->
<input type="file" class="upload" />
<img src="" alt="" /><script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>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)document.querySelector("img").src = result.data.data.url;});});
</script>
  • 示例:更换背景图片
<div class="container"><div class="nav"><div class="left"><ul><li><a href="http://yun.itheima.com/?webzly" target="_blank" rel="nofollow">免费教程</a></li><li><a href="http://resource.ityxb.com/booklist/?webzly" target="_blank" rel="nofollow">原创书籍</a></li><li><a href="http://www.itheima.com/teacher.html?webzly#ajavaee" target="_blank" rel="nofollow">教研团队</a></li><li><a href="http://www.itheima.com/special/hmschool/index.shtml?webzly" target="_blank" rel="nofollow">校区汇总</a></li><li><a href="http://www.itheima.com/flow/flow.html?webzly" target="_blank" rel="nofollow">报名流程</a></li><li><a href="https://pip.itcast.cn?hmgw$webzly" target="_blank" rel="nofollow">项目信息站</a></li><li><a href="http://bbs.itheima.com/forum.php?webzly" target="_blank" rel="nofollow">技术社区</a></li></ul></div><div class="right"><label for="bg">更换背景</label><input class="bg-ipt" type="file" id="bg" /></div></div><div class="search-container"><img src="https://www.itheima.com/images/logo.png" alt="" /><div class="search-box"><input type="text" /><button>搜索一下</button></div></div>
</div>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<!-- 核心代码 -->
<script src="./js/index.js"></script>
  • index.js
document.querySelector(".bg-ipt").addEventListener("change", e => {// 1. 选择图片上传,设置body背景const fd = new FormData();fd.append("img", e.target.files[0]);axios({url: "http://hmajax.itheima.net/api/uploadimg",method: "post",data: fd,}).then(result => {// console.log(result)const imgUrl = result.data.data.url;document.body.style.backgroundImage = `url(${imgUrl})`;// 2. 上传成功时,"保存"图片url网址localStorage.setItem("bgImg", imgUrl);});
});// 3. 网页运行后,获取url网址使用
const imgUrl = localStorage.getItem("bgImg");
document.body.style.backgroundImage = `url(${imgUrl})`;

2.9 案例-个人信息设置

  • 结构
<!-- toast 提示框 -->
<div class="toast my-toast" data-bs-delay="1500"><div class="toast-body"><div class="alert alert-success info-box">操作成功</div></div>
</div>
<!-- 核心内容区域 -->
<div class="container"><ul class="my-nav"><li class="active">基本设置</li><li>安全设置</li><li>账号绑定</li><li>新消息通知</li></ul><div class="content"><div class="info-wrap"><h3 class="title">基本设置</h3><form class="user-form" action="javascript:;"><div class="form-item"><label for="email">邮箱</label><input id="email" name="email" class="email" type="text" placeholder="请输入邮箱" autocomplete="off"></div><div class="form-item"><label for="nickname">昵称</label><input id="nickname" name="nickname" class="nickname" type="text" placeholder="请输入昵称" autocomplete="off"></div><div class="form-item"><label>性别</label><label class="male-label"><input type="radio" name="gender" class="gender" value="0">男</label><label class="male-label"><input type="radio" name="gender" class="gender" value="1">女</label></div><div class="form-item"><label for="desc">个人简介</label><textarea id="desc" name="desc" class="desc" placeholder="请输入个人简介" cols="20" rows="10" autocomplete="off"></textarea></div><button class="submit">提交</button></form></div><div class="avatar-box"><h4 class="avatar-title">头像</h3><img class="prew" src="./img/头像.png" alt=""><label for="upload">更换头像</label><input id="upload" type="file" class="upload"></div></div>
</div>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.min.js"></script>
<script src="./lib/form-serialize.js"></script>
<!-- 核心逻辑 -->
<script src="./js/index.js"></script>
  • index.js

  • 信息渲染

/*** 目标1:信息渲染*  1.1 获取用户的数据*  1.2 回显数据到标签上* */const creator = "小明";
axios({url: "http://hmajax.itheima.net/api/settings",params: {creator,},
}).then(result => {// console.log(result.data.data)const userObj = result.data.data;const keys = Object.keys(userObj);keys.forEach(key => {// 头像和性别比较特殊,需要单独处理if (key === "avatar") {document.querySelector(".prew").src = userObj[key];} else if (key === "gender") {const gRadioList = document.querySelectorAll(".gender");// 0男,1女,刚好与数组下标对应const gNum = userObj[key];gRadioList[gNum].checked = true;} else {document.querySelector(`.${key}`).value = userObj[key];}});
});
  • 修改头像
/*** 目标2:修改头像*  2.1 获取头像文件*  2.2 提交服务器并更新头像*/document.querySelector(".upload").addEventListener("change", e => {const fd = new FormData();fd.append("img", e.target.files[0]);fd.append("creator", creator);axios({url: "http://hmajax.itheima.net/api/avatar",method: "put",data: fd,}).then(result => {document.querySelector(".prew").src = result.data.data.avatar;});
});
  • 修改数据,并提示
/*** 目标3:提交表单*  3.1 收集表单信息*  3.2 交到服务器保存*//*** 目标4:结果提示*  4.1 创建toast对象*  4.2 调用show方法->显示提示框*/document.querySelector(".submit").addEventListener("click", () => {// 3.1 收集表单信息const form = document.querySelector(".user-form");const formData = serialize(form, { hash: true, empty: true });formData.creator = creator;formData.gender = +formData.gender;// 3.2 提交到服务器保存axios({url: "http://hmajax.itheima.net/api/settings",method: "put",data: formData,}).then(result => {// 4.1 创建toast对象const toastDom = document.querySelector(".my-toast");const toast = new bootstrap.Toast(toastDom);// 4.2 调用show方法->显示提示框toast.show();});
});

2.10 请求方式别名

  • 为了方便起见,已经为所有支持的请求方法提供了别名
axios.request(config)
axios.get(url[, config])
axios.delete(url[, config])
axios.head(url[, config])
axios.options(url[, config])
axios.post(url[, data[, config]])
axios.put(url[, data[, config]])
  • 注:在使用别名方法时, urlmethoddata 这些属性都不必在配置中指定

2.11 Axios API

2.11.1 axios 实例

可以使用自定义配置新建一个实例

const instance = axios.create({baseURL: "https://some-domain.com/api/",timeout: 1000,headers: { "X-Custom-Header": "foobar" },
});
  • 一些实例方法

    • axios#request(config)

    • axios#get(url[, config])

    • axios#delete(url[, config])

    • axios#head(url[, config])

    • axios#options(url[, config])

    • axios#post(url[, data[, config]])

    • axios#put(url[, data[, config]])

    • axios#patch(url[, data[, config]])

    • axios#getUri([config])

2.11.2 请求配置

  • 这些是创建请求时可以用的配置选项,只有url是必需的

  • 若没指定method,请求默认使用GET

{// url:用于请求的服务器URLurl: '/user',// method:创建请求时使用的方法,默认为getmethod: 'get',// baseURL:自动加在url前面,通过它可以传递相对地址baseURL: 'https://some-domain.com/api/',// transformRequest:允许在向服务器发送前,修改请求数据// 它只能用于'PUT', 'POST'和'PATCH'这几个请求方法// 数组中最后一个函数必须返回一个字符串, 一个Buffer实例,ArrayBuffer,FormData或Stream// 可以修改请求头transformRequest: [function (data, headers) {// 对发送的 data 进行任意转换处理return data;}],// transformResponse:在传递给then/catch前,允许修改响应数据transformResponse: [function (data) {// 对接收的 data 进行任意转换处理return data;}],// 自定义请求头headers: {'X-Requested-With': 'XMLHttpRequest'},// params:与请求一起发送的URL参数// 必须是一个简单对象或URLSearchParams对象params: {ID: 12345},// data:作为请求体被发送的数据// 仅适用'PUT', 'POST', 'DELETE'和'PATCH'请求方法// 在没有设置`transformRequest`时,则必须是以下类型之一:// - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams// - 浏览器专属: FormData, File, Blob// - Node 专属: Stream, Bufferdata: {firstName: 'Fred'},// 发送请求体数据的可选语法// 请求方式 post// 只有 value 会被发送,key 则不会data: 'Country=Brasil&City=Belo Horizonte',// timeout:指定请求超时的毫秒数// 如果请求时间超过`timeout`的值,则请求会被中断,默认为0,表示永不超时timeout: 1000,// withCredentials:表示跨域请求时是否需要使用凭证,默认falsewithCredentials: false,// adapter:允许自定义处理请求,这使测试更加容易// 返回一个`promise`并提供一个有效的响应(参见 lib/adapters/README.md)adapter: function (config) {/* ... */},// auth:HTTP Basic Authauth: {username: 'janedoe',password: 's00pers3cret'},// responseType:浏览器将要响应的数据类型// 选项包括: 'arraybuffer', 'document', 'json', 'text', 'stream'// 浏览器专属:'blob'responseType: 'json', // 默认值// responseEncoding:用于解码响应的编码 (Node.js专属)// 注意:忽略`responseType`的值为 'stream',或者是客户端请求responseEncoding: 'utf8', // 默认值// `xsrfCookieName`是`xsrf token`的值,被用作`cookie`的名称xsrfCookieName: 'XSRF-TOKEN', // 默认值// `xsrfHeaderName`是带有`xsrf token`值的http请求头名称xsrfHeaderName: 'X-XSRF-TOKEN', // 默认值// `onUploadProgress`允许为上传处理进度事件(浏览器专属)onUploadProgress: function (progressEvent) {// 处理原生进度事件},// `onDownloadProgress`允许为下载处理进度事件(浏览器专属)onDownloadProgress: function (progressEvent) {// 处理原生进度事件},// `maxContentLength`定义了node.js中允许的HTTP响应内容的最大字节数maxContentLength: 2000,// `maxBodyLength`(仅Node)定义允许的http请求内容的最大字节数maxBodyLength: 2000,// `validateStatus`定义了对于给定的HTTP状态码是 resolve 还是 reject promise// 返回`true`(或者设置为`null`或`undefined`),则promise将会resolved,否则是rejectedvalidateStatus: function (status) {return status >= 200 && status < 300; // 默认值},// maxRedirects:定义了在node.js中要遵循的最大重定向数,设置为0,则不会进行重定向maxRedirects: 5, // 默认值// `socketPath` 定义了在node.js中使用的UNIX套接字// e.g. '/var/run/docker.sock' 发送请求到 docker 守护进程// 只能指定 `socketPath` 或 `proxy`// 若都指定,则使用 `socketPath`socketPath: null, // default// `httpAgent`和`httpsAgent`分别定义了在 node.js 中执行 http 和 https 请求时使用的自定义代理// 这样就可以添加默认情况下未启用的选项,如`keepAlive`httpAgent: new http.Agent({ keepAlive: true }),httpsAgent: new https.Agent({ keepAlive: true }),// `proxy` 定义了代理服务器的主机名,端口和协议// 可以使用常规的`http_proxy`和`https_proxy` 环境变量// 使用`false`可以禁用代理功能,同时环境变量也会被忽略// `auth`表示应使用HTTP Basic auth连接到代理,并且提供凭据// 这将设置一个`Proxy-Authorization`请求头,它会覆盖`headers`中已存在的自定义`Proxy-Authorization`请求头// 如果代理服务器使用 HTTPS,则必须设置 protocol 为`https`proxy: {protocol: 'https',host: '127.0.0.1',port: 9000,auth: {username: 'mikeymike',password: 'rapunz3l'}},// 详见https://axios-http.com/zh/docs/cancellationcancelToken: new CancelToken(function (cancel) {}),// `decompress`:表示是否要自动解压缩响应正文// 将其设置为"false",它不会解压缩响应,并会保留原始的"Content-Encoding"标头// 如果设置为“true”,将从所有解压缩响应的响应对象中移除"Content-Encoding"标头// - 仅适用于Node (XHR 无法关闭解压缩功能)decompress: true // 默认值
}

2.11.3 响应结构

  • 一个请求的响应包含以下信息
{// data:由服务器提供的响应data: {},// status:来自服务器响应的 HTTP 状态码status: 200,// statusText:来自服务器响应的 HTTP 状态信息statusText: 'OK',// headers:服务器响应头// 所有 header 名称都是小写,且可以使用方括号语法访问// 例如: `response.headers['content-type']`headers: {},// config:是`axios`请求的配置信息config: {},// request:是生成此响应的请求// 在node.js中它是最后一个ClientRequest实例 (in redirects),// 在浏览器中则是 XMLHttpRequest 实例request: {}
}

2.11.4 默认配置

🛠️ 全局 axios 默认值
axios.defaults.baseURL = "https://api.example.com";
axios.defaults.headers.common["Authorization"] = AUTH_TOKEN;
axios.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded";
🛠️ 自定义实例默认值
// 创建实例时配置默认值
const instance = axios.create({baseURL: "https://api.example.com",
});// 创建实例后修改默认值
instance.defaults.headers.common["Authorization"] = AUTH_TOKEN;
🛠️ 配置的优先级
  • 配置将会按优先级进行合并

  • 它的顺序是:在lib/defaults.js中找到的库默认值,然后是实例的 defaults 属性,最后是请求的config参数,后面的优先级要高于前面的

// 使用库提供的默认配置创建实例
// 此时超时配置的默认值是 `0`
const instance = axios.create();// 重写库的超时默认值
// 现在,所有使用此实例的请求都将等待2.5秒,然后才会超时
instance.defaults.timeout = 2500;// 重写此请求的超时时间,因为该请求需要很长时间
instance.get("/longRequest", {timeout: 5000,
});

2.11.5 拦截器

  • 在请求或响应被 then 或 catch 处理前拦截它们
// 添加请求拦截器
axios.interceptors.request.use(function (config) {// 在发送请求之前做些什么return config;},function (error) {// 对请求错误做些什么return Promise.reject(error);}
);// 添加响应拦截器
axios.interceptors.response.use(function (response) {// 2xx 范围内的状态码都会触发该函数// 对响应数据做点什么return response;},function (error) {// 超出 2xx 范围的状态码都会触发该函数// 对响应错误做点什么return Promise.reject(error);}
);
  • 移除拦截器
const myInterceptor = axios.interceptors.request.use(function () {/*...*/
});
axios.interceptors.request.eject(myInterceptor);
  • 可以给自定义的 axios 实例添加拦截器
const instance = axios.create();
instance.interceptors.request.use(function () {/*...*/
});

2.11.6 错误处理

axios.get("/user/12345").catch(function (error) {if (error.response) {// 请求成功发出且服务器也响应了状态码,但状态代码超出了 2xx 的范围console.log(error.response.data);console.log(error.response.status);console.log(error.response.headers);} else if (error.request) {// 请求已经成功发起,但没有收到响应// `error.request` 在浏览器中是 XMLHttpRequest 的实例,// 而在node.js中是 http.ClientRequest 的实例console.log(error.request);} else {// 发送请求时出了点问题console.log("Error", error.message);}console.log(error.config);
});
  • 使用 validateStatus 配置选项,可以自定义抛出错误的 HTTP code
axios.get("/user/12345", {validateStatus: function (status) {return status < 500; // 处理状态码小于500的情况},
});
  • 使用 toJSON 可以获取更多关于 HTTP 错误的信息
axios.get("/user/12345").catch(function (error) {console.log(error.toJSON());
});

2.11.7 取消请求

🛠️ AbortController
const controller = new AbortController();axios.get("/foo/bar", {signal: controller.signal,}).then(function (response) {//...});
// 取消请求
controller.abort();

2.11.8 请求体编码

  • 默认情况下,axios 将 JavaScript 对象序列化为JSON。 要以application/x-www-form-urlencoded格式发送数据
🛠️ 浏览器
  • URLSearchParams
const params = new URLSearchParams();
params.append("param1", "value1");
params.append("param2", "value2");
axios.post("/foo", params);
  • 注:不是所有的浏览器都支持 URLSearchParams ,但是可以使用polyfill (确保 polyfill 全局环境)

  • 可以使用qs 库编码数据

import qs from "qs";
const data = { bar: 123 };
const options = {method: "POST",headers: { "content-type": "application/x-www-form-urlencoded" },data: qs.stringify(data),url,
};
axios(options);
🛠️ Node.js
  • 在 node.js 中, 可以使用querystring 模块
const querystring = require("querystring");
axios.post("http://something.com/", querystring.stringify({ foo: "bar" }));
  • 或者从'url module'中使用'URLSearchParams'
const url = require("url");
const params = new url.URLSearchParams({ foo: "bar" });
axios.post("http://something.com/", params.toString());
  • 也可以使用 qs 库

  • Form data

const FormData = require("form-data");const form = new FormData();
form.append("my_field", "my value");
form.append("my_buffer", new Buffer(10));
form.append("my_file", fs.createReadStream("/foo/bar.jpg"));axios.post("https://example.com", form, { headers: form.getHeaders() });
  • 或者, 使用一个拦截器
axios.interceptors.request.use(config => {if (config.data instanceof FormData) {Object.assign(config.headers, config.data.getHeaders());}return config;
});

2.11.9 axios 二次封装

  • 为什么

    • 方便管理和维护

    • 请求的 url 地址统一管理

    • 某些接口需要传递 headers

  • request.js

// 1. 引入axios
import axios from 'axios';
// 2. 创建axios对象
const service = axios.create();
// 3. 请求拦截器(前端给后端发送数据,没有到后端)
// 做的事:headers给后端传递token
service.interceptors.request.use(config => {return config;}, error => {Promise.reject(error);}
});
// 4.响应拦截器(后端给前端返回数据,前端到后端了)
service.interceptors.response.use((response) => {// 这里是对响应的简化 data = response.dataconst { data, meta } = response.dataif (meta.status === 200 || meta.status === 201){//回传的数据return data} else {ElMessage.error(meta.msg)return Promise.reject(new Error(meta.msg))}},error => {error.response && ElMessage.error(error.response.data)return Promise.reject(new Error(error.response.data))}
)
export default service

2.11.10 API 解耦

  • 封装
import request from "@/utils/request";
export function getsliders() {return request({url: "/api/slider/getsliders",});
}
  • 使用
<script type="text/javascript">import {getSliders} from '@/utils/api/course'export default{data (){return {list:[]}},created() {getsliders().then (res=>{console.log(res)})}
</script>

3. Ajax 原理

3.1 XMLHttpRequest

  • Ajax 是浏览器与服务器通信的技术,采用 XMLHttpRequest 对象相关代码

  • axios 是对 XHR 相关代码进行了封装,让我们只关心传递的接口参数

  • 学习 XHR 也是了解 axios 内部与服务器交互过程的真正原理

  • 语法:

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

  • 示例 1:获取所有省份列表并展示到页面上
<p class="my-p"></p>
<script>/*** 目标:使用XMLHttpRequest对象与服务器通信*  1. 创建 XMLHttpRequest 对象*  2. 配置请求方法和请求 url 地址*  3. 监听 loadend 事件,接收响应结果*  4. 发起请求*/const xhr = new XMLHttpRequest();xhr.open("get", "http://hmajax.itheima.net/api/province");// 携带查询参数// xhr.open('get', 'http://hmajax.itheima.net/api/city?pname=辽宁省')xhr.addEventListener("loadend", () => {// console.log(xhr.response)const data = JSON.parse(xhr.response);document.querySelector(".my-p").innerHTML = data.list.join("<br>");});xhr.send();
</script>
  • 示例 2:地区查询
<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>/*** 目标: 根据省份和城市名字, 查询对应的地区列表*/document.querySelector(".sel-btn").addEventListener("click", () => {const pname = document.querySelector(".province").value;const cname = document.querySelector(".city").value;paramsObj = new URLSearchParams({pname,cname,});const xhr = new XMLHttpRequest();xhr.open("get", `http://hmajax.itheima.net/api/area?${paramsObj}`);xhr.addEventListener("loadend", () => {const data = JSON.parse(xhr.response);document.querySelector(".list-group").innerHTML = data.list.map(areaName => {return `<li class="list-group-item">${areaName}</li>`;}).join("");});xhr.send();});
</script>
  • 示例 3:数据提交
<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);});// 设置请求头xhr.setRequestHeader("content-type", "application/json");// 准备提交的数据const userObj = {username: "itheima667",password: "123456",};const userStr = JSON.stringify(userObj);xhr.send(userStr);});
</script>

3.2 Promise

3.2.1 对于异步的理解

  • 示例
console.log("开始"); // 立即执行,输出 "开始"setTimeout(() => {// 设置一个异步任务,延迟 1000 毫秒后执行console.log("异步任务完成"); // 这个代码在 1 秒后执行
}, 1000);console.log("结束"); // 立即执行,输出 "结束"
  • 输出顺序

    • 开始:第一个console.log立即执行,输出"开始"

    • 结束:第二个console.log立即执行,输出"结束"

    • 异步任务完成:在设置的 1 秒之后,setTimeout中的回调函数才被调用,输出"异步任务完成"

  • 异步体现

    • 非阻塞:在setTimeout调用后,代码并没有等待 1 秒,而是继续执行后面的console.log('结束')。这说明setTimeout是非阻塞的

    • 执行顺序:即使setTimeout设置了一个 1 秒的延迟,它的回调函数并不会立即执行,而是被放入事件队列中,等待主线程空闲时再执行。这导致"结束"会在"异步任务完成"之前输出

  • 总结

    • 异步允许代码在等待某个操作(如定时器)时继续执行其他代码,而不会阻塞整个程序的执行

3.2.2 概述

  • 是什么

    • 表示(管理)一个异步操作最终状态和结果值的对象
  • Promise 的好处是什么?

    • 逻辑更清晰(成功或失败会关联后续的处理函数)

    • 了解 axios 函数内部运作的机制

    • 解决回调函数地狱问题

  • Promise 管理异步任务的语法

// 1. 创建 Promise 对象
const p = new Promise((resolve, reject) => {// 2. 执行异步任务-并传递结果// 成功调用: resolve(值) 触发 then() 执行// 失败调用: reject(值) 触发 catch() 执行
});
// 3. 接收结果
p.then(result => {// 成功
}).catch(error => {// 失败
});
  • 示例
/*** 目标:使用Promise管理异步任务*/
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);
});

3.2.3 三种状态

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

  • 已兑现(fulfilled):操作成功完成

  • 已拒绝(rejected):操作失败

  • 改变 Promise 对象状态后,内部触发对应回调函数传参并执行

  • 注:每个 Promise 对象一旦被兑现/拒绝,那么状态无法再被改变

3.2.4 案例-获取省份列表

<p class="my-p"></p>
<script>const p = new Promise((resolve, reject) => {const xhr = new XMLHttpRequest();xhr.open("get", "http://hmajax.itheima.net/api/province");xhr.addEventListener("loadend", () => {// 判断请求成功与否if (xhr.status >= 200 && xhr.status < 300) {resolve(JSON.parse(xhr.response));} else {reject(new Error(xhr.response));}});xhr.send();});p.then(reslut => {document.querySelector(".my-p").innerHTML = reslut.list.join("<br>");}).catch(error => {// 打印详细信息// console.dir(error)document.querySelector(".my-p").innerHTML = error.message;});
</script>

3.2.5 封装简易 axios

  • 步骤

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

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

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

    • 使用 myAxios 函数,获取省份列表展示

  • 核心语法

function myAxios(config) {return new Promise((resolve, reject) => {// XHR 请求// 调用成功/失败的处理程序});
}myAxios({url: "目标资源地址",
}).then(result => {}).catch(error => {});
  • 示例:获取省份列表
<p class="my-p"></p>
<script>function myAxios(config) {return new Promise((resolve, reject) => {const xhr = new XMLHttpRequest();xhr.open(config.method || "get", config.url);xhr.addEventListener("loadend", () => {if (xhr.status >= 200 && xhr.status < 300) {resolve(JSON.parse(xhr.response));} else {reject(new Error(xhr.response));}});xhr.send();});}myAxios({url: "http://hmajax.itheima.net/api/province",}).then(result => {document.querySelector(".my-p").innerHTML = result.list.join("<br>");}).catch(error => {document.querySelector(".my-p").innerHTML = error.message;});
</script>
  • 示例:获取地区列表
<p class="my-p"></p>
<script>function myAxios(config) {return new Promise((resolve, reject) => {const xhr = new XMLHttpRequest();// 查询参数if (config.params) {const paramsObj = new URLSearchParams(config.params);const paramsStr = paramsObj.toString();config.url += `?${paramsStr}`;}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 => {document.querySelector(".my-p").innerHTML = result.list.join("<br>");}).catch(error => {document.querySelector(".my-p").innerHTML = error.message;});
</script>
  • 示例:注册用户
<p class="my-p"></p>
<script>function myAxios(config) {return new Promise((resolve, reject) => {const xhr = new XMLHttpRequest();// 查询参数if (config.params) {const paramsObj = new URLSearchParams(config.params);const paramsStr = paramsObj.toString();config.url += `?${paramsStr}`;}xhr.open(config.method || "get", config.url);xhr.addEventListener("loadend", () => {if (xhr.status >= 200 && xhr.status < 300) {resolve(JSON.parse(xhr.response));} else {reject(new Error(xhr.response));}});if (config.data) {xhr.setRequestHeader("content-type", "application/json");const jsonStr = JSON.stringify(config.data);xhr.send(jsonStr);} else {xhr.send();}});}myAxios({url: "http://hmajax.itheima.net/api/register",method: "post",data: {username: "itheima776",password: "66688879",},}).then(result => {document.querySelector(".my-p").innerHTML = result.message;}).catch(error => {document.querySelector(".my-p").innerHTML = error.message;});
</script>

3.2.6 回调函数地狱

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

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

<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>/*** 目标:演示回调函数地狱* 需求:获取默认第一个省,第一个市,第一个地区并展示在下拉菜单中*/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 => {const aname = result.data.list[0];document.querySelector(".area").innerHTML = aname;});});}).catch(error => {console.log(error);});
</script>

3.2.7 Promise 链式调用

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

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

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

const p = new Promise((resolve, reject) => {setTimeout(() => {resolve("北京市");}, 1000);
});const p2 = p.then(result => {console.log(result);return new Promise((resolve, reject) => {setTimeout(() => {resolve(`${result} --- 北京`);}, 1000);});
});p2.then(result => {console.log(result);
});
  • 解决回调函数地狱
<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>let pname = "";axios({ url: "http://hmajax.itheima.net/api/province" }).then(result => {pname = result.data.list[0];document.querySelector(".province").innerHTML = pname;return axios({ url: "http://hmajax.itheima.net/api/city", params: { pname } });}).then(result => {const cname = result.data.list[0];document.querySelector(".city").innerHTML = cname;return axios({ url: "http://hmajax.itheima.net/api/area", params: { pname, cname } });}).then(result => {const aname = result.data.list[0];document.querySelector(".area").innerHTML = aname;});
</script>

3.2.8 async 函数和 await

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

<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 function getData() {const pObj = await axios({ url: "http://hmajax.itheima.net/api/province" });const pname = pObj.data.list[0];const cObj = await axios({ url: "http://hmajax.itheima.net/api/city", params: { pname } });const cname = cObj.data.list[0];const aObj = await axios({ url: "http://hmajax.itheima.net/api/area", params: { pname, cname } });const aname = aObj.data.list[0];document.querySelector(".province").innerHTML = pname;document.querySelector(".city").innerHTML = cname;document.querySelector(".area").innerHTML = aname;}getData();
</script>
  • 捕获错误
try {// 要执行的代码// 如果try里某行代码报错后,try中剩余的代码不会执行了
} catch (error) {// error 接收的是错误消息;// try 里代码,如果有错误,直接进入这里执行
}
  • 示例
<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 function getData() {try {const pObj = await axios({ url: "http://hmajax.itheima.net/api/province" });const pname = pObj.data.list[0];const cObj = await axios({ url: "http://hmajax.itheima.net/api/city", params: { pname } });const cname = cObj.data.list[0];const aObj = await axios({ url: "http://hmajax.itheima.net/api/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) {console.dir(error);}}getData();
</script>

3.3 事件循环(EventLoop)

  • 作用:事件循环负责执行代码,收集和处理事件以及执行队列中的子任务

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

  • 概念:执行代码和收集异步任务的模型,在调用栈空闲时,反复调用任务队列里回调函数的执行机制

  • JavaScript 内代码如何执行?

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

/*** 目标:阅读并回答执行的顺序结果*/
console.log(1);
setTimeout(() => {console.log(2);
}, 0);
function myFn() {console.log(3);
}
function ajaxFn() {const xhr = new XMLHttpRequest();xhr.open("GET", "http://hmajax.itheima.net/api/province");xhr.addEventListener("loadend", () => {console.log(4);});xhr.send();
}
for (let i = 0; i < 1; i++) {console.log(5);
}
ajaxFn();
document.addEventListener("click", () => {console.log(6);
});
myFn();

结果:1 5 3 2 4 点击一次 document 就会执行一次打印 6

3.4 宏任务与微任务

  • 异步任务分为

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

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

  • 事件循环模型
console.log(1);
setTimeout(() => {console.log(2);
}, 0);
const p = new Promise((resolve, reject) => {resolve(3);
});
p.then(res => {console.log(res);
});
console.log(4);

  • 注:宏任务每次在执行同步代码时,产生微任务队列,清空微任务队列任务后,微任务队列空间释放!下一次宏任务执行时,遇到微任务代码,才会再次申请微任务队列空间放入回调函数消息排队

  • 总结:一个宏任务包含微任务队列,他们之间是包含关系,不是并列关系

  • 经典面试题

// 目标:回答代码执行顺序
console.log(1);
setTimeout(() => {console.log(2);const p = new Promise(resolve => resolve(3));p.then(result => console.log(result));
}, 0);
const p = new Promise(resolve => {setTimeout(() => {console.log(4);}, 0);resolve(5);
});
p.then(result => console.log(result));
const p2 = new Promise(resolve => resolve(6));
p2.then(result => console.log(result));
console.log(7);

结果:1 7 5 6 2 3 4

3.5 Promise.all 静态方法

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

  • 语法:
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>
  • 案例:商品分类

    • 目标:把所有商品分类“同时”渲染到页面上

    • 获取所有一级分类数据

    • 遍历 id,创建获取二级分类请求

    • 合并所有二级分类 Promise 对象

    • 等待同时成功后,渲染页面

<!-- 大容器 -->
<div class="container"><div class="sub-list"></div>
</div>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>// 1. 获取所有一级分类数据axios({url: "http://hmajax.itheima.net/api/category/top",}).then(result => {// console.log(result)const secPromiseList = result.data.data.map(item => {// 2. 遍历id,创建获取二级分类请求return axios({url: "http://hmajax.itheima.net/api/category/sub",params: {id: item.id,},});});const p = Promise.all(secPromiseList);p.then(result => {// console.log(result)document.querySelector(".sub-list").innerHTML = 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>`;}).join("")}</ul></div>`;}).join("");});});
</script>
  • 案例:学习反馈

  • 框架

<div class="container"><h4 class="stu-title">学习反馈</h4><img class="bg" src="./img/head.png" alt="" /><div class="item-wrap"><div class="hot-area"><span class="hot">热门校区</span><ul class="nav"><li><a target="_blank" href="http://bjcp.itheima.com/">北京</a></li><li><a target="_blank" href="http://sh.itheima.com/">上海</a></li><li><a target="_blank" href="http://gz.itheima.com/">广州</a></li><li><a target="_blank" href="http://sz.itheima.com/">深圳</a></li></ul></div><form class="info-form"><div class="area-box"><span class="title">地区选择</span><select name="province" class="province"><option value="">省份</option></select><select name="city" class="city"><option value="">城市</option></select><select name="area" class="area"><option value="">地区</option></select></div><div class="area-box"><span class="title">您的称呼</span><input type="text" name="nickname" class="nickname" value="播仔" /></div><div class="area-box"><span class="title">宝贵建议</span><textarea type="text" name="feedback" class="feedback" placeholder="您对AJAX阶段课程宝贵的建议"></textarea></div><div class="area-box"><button type="button" class="btn btn-secondary submit">确定提交</button></div></form></div>
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/1.2.0/axios.min.js"></script>
<script src="./js/form-serialize.js"></script>
<!-- 核心代码 -->
<script src="./js/index.js"></script>
  • index.js
/*** 目标1:完成省市区下拉列表切换*  1.1 设置省份下拉菜单数据*  1.2 切换省份,设置城市下拉菜单数据,清空地区下拉菜单*  1.3 切换城市,设置地区下拉菜单数据*/
// 1.1 设置省份下拉菜单数据
axios({url: "http://hmajax.itheima.net/api/province",
}).then(result => {const str = result.data.list.map(pname => `<option value="${pname}">${pname}</option>`).join("");document.querySelector(".province").innerHTML = `<option value="">省份</option>` + str;
});// 1.2 切换省份,设置城市下拉菜单数据,清空地区下拉菜单
document.querySelector(".province").addEventListener("change", async e => {const result = await axios({url: "http://hmajax.itheima.net/api/city",params: {pname: e.target.value,},});// console.log(result)const str = result.data.list.map(cname => {return `<option value="${cname}">${cname}</option>`;}).join("");document.querySelector(".city").innerHTML = `<option value="">城市</option>` + str;// 清空地区下拉菜单document.querySelector(".area").innerHTML = `<option value="">地区</option>`;
});// 1.3 切换城市,设置地区下拉菜单数据
document.querySelector(".city").addEventListener("change", async e => {const result = await axios({url: "http://hmajax.itheima.net/api/area",params: {pname: document.querySelector(".province").value,cname: e.target.value,},});const str = result.data.list.map(aname => `<option value="${aname}">${aname}</option>`);document.querySelector(".area").innerHTML = `<option value="">地区</option>` + str;
});
/*** 目标2:收集数据提交保存*  2.1监听提交的点击事件*  2.2依靠插件收集表单数据*  2.3基于axios提交保存,显示结果*/document.querySelector(".submit").addEventListener("click", async () => {try {const form = document.querySelector(".info-form");const data = serialize(form, { hash: true, empty: true });const result = await axios({url: "http://hmajax.itheima.net/api/feedback",method: "post",data,});alert(result.data.message);} catch (error) {// console.dir(error)alert(error.response.data.message);}
});

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/861454.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

超低功耗段LCD液晶段码显示屏驱动芯片(ic)VKL128 LQFP44 I2C通信接口/可配置4种功耗模式

产品品牌:永嘉微电/VINKA 产品型号:VKL128 封装形式:SSOP44 概述 VKL128是一个点阵式存储映射的LCD驱动器,可支持最大128点(32SEGx4COM)的LCD屏。单片机可通过I2C接口配置显示参数和读写显示数据,可配置4种功耗模式,也可通过关显示和关振荡器进入省电模式。其高抗干扰,…

私有化部署视频平台EasyCVR安防小知识:如何评估一个监控系统的抗干扰性能?

在当今复杂多变的监控环境中,确保监控系统的稳定性和可靠性至关重要。抗干扰性能作为衡量监控系统性能的关键指标之一,直接关系到监控图像的清晰度、数据的完整性以及系统的响应速度。 本文将详细介绍评估监控系统抗干扰性能的多个关键方面,以及如何通过这些评估来优化系统性…

离线安装Kubesphere

1.环境要求 【centos7.X】 1.1依赖项要求 master、node1节点安装 yum install -y socat conntrack ebtables ipset1.2获取镜像列表访问 https://get-images.kubesphere.io/ 选择需要部署的扩展组件。 填入邮箱地址。 点击获取镜像列表。 查看填写的邮箱,获取 KubeSphere 最新的…

@antv/g2 使用小结

版本:5.2.10 一、引入@antv/g2 import { Chart } from "@antv/g2";二、初始化 # container里类似echarts,可以用id也可以用refs let chart = new Chart({ container: "pie-chart", autoFit: true }); # 如果chart已存在,就是重复渲染的情况 if (chart) …

加分博客

恋爱对象陆思梦,高中同学,暗黑女神(爱称,因为我是迪迦)。 恋爱过程高中应该怎么说呢,我跟她是高中同学,那个时候还没有喜欢她,而是班上的另一个女孩,杨sir,跟杨sir表白被拒后,就没然后了。高中的时候,只觉得她是一个开朗活泼的女孩,她还有个妹妹,是双胞胎,两个…

python 资源管理工具V1

python 资源管理工具V1 资源管理工具V1界面python 3 环境安装 python -m pip install configparser==5.3.0 -i https://pypi.tuna.tsinghua.edu.cn/simple/ python -m pip install pymysql==0.9.3 -i https://pypi.tuna.tsinghua.edu.cn/simple/ python -m pip install pyper…

基于时间轴的项目进度管理工具

在项目管理中,时间轴是一种强大的工具,它能够直观地展示项目的进度、任务的先后顺序以及时间的推移。通过时间轴,项目团队可以清晰地把握项目的整体节奏,及时发现潜在问题,做出合理的决策。市面上基于时间轴的项目进度管理工具众多,每一款都有其独特的特点和优势。本文将…

(数据科学学习手札164)在vscode中调用Deepseek进行AI辅助编程

本文示例配置文件已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes1 简介大家好我是费老师,最近国产大模型Deepseek v3新版本凭借其优秀的模型推理能力,讨论度非常之高🔥,且其官网提供的相关大模型API接口服务价格一直走的“价格屠夫”路线,性…

【4】0x10服务27服务

1. 0x10服务之诊断会话控制 诊断仪与ECU之间的诊断通信过程就相当于两个人的会话过程,你一言,我一语,会话可以在不同的场景下进行。 0x10服务即诊断会话控制,规定了默认会话(01)、拓展会话(03)、编程会话(02),三种常见的诊断会话。 默认会话:22、14、19、11服务等。…

阳大壮AI助手

你还在为选择哪个AI产品而苦恼吗?试试哥们这个阳大壮http://47.95.159.19:9090/