01-综合案例:图书管理
/*思路分析
1.页面一加载,ajax请求所有图书列表
- 技术点:数据驱动渲染页面
2.点击添加按钮 : 添加图书
- 技术点: 获取表单里面的参数
3.点击删除按钮 : 删除图书
- 技术点: 动态新增按钮无法直接注册事件,需要使用 事件委托
4.页面所有的ajax请求都需要loading效果
- 技术点: axios拦截器使用
*/
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<!-- 引入 lib 目录下的 bootstrap 样式表 -->
<link rel="stylesheet" href="./lib/bootstrap-v4.6.0.css" />
<style>
:root {
font-size: 15px;
}
body {
padding-top: 15px;
}
.loading-box {
position: fixed;
top: 30%;
left: 50%;
transform: translateX(-50%);
background-color: rgba(0, 0, 0, 0.1);
border-radius: 10px;
box-shadow: 1px 1px 3px #efefef;
display: none;
}
</style>
</head>
<body>
<!-- 栅格系统 -->
<div class="container-fluid">
<!-- 栅格系统中的一行 -->
<div class="row">
<!-- 左侧的表格,占了 8 列 -->
<div class="col-sm-8">
<table
class="table table-bordered table-striped table-dark table-hover text-center"
>
<thead>
<!-- 表头行 -->
<tr>
<th scope="col">Id</th>
<th scope="col">书名</th>
<th scope="col">作者</th>
<th scope="col">出版社</th>
<th scope="col">操作</th>
</tr>
</thead>
<tbody>
<!-- 表格中的每一行 -->
<tr>
<th scope="row">xxx</th>
<td>xxx</td>
<td>xxx</td>
<td>xxx</td>
<td>
<button type="button" class="btn btn-link btn-sm">
删除
</button>
</td>
</tr>
</tbody>
</table>
</div>
<!-- 右侧的添加区域,占了 4 列 -->
<div class="col-sm-4">
<!-- 添加图书的卡片 -->
<div class="card text-white bg-secondary sticky-top">
<div class="card-header">添加新图书</div>
<form class="card-body bg-light" id="addForm">
<!-- 书名 -->
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text">书名</span>
</div>
<input
type="text"
class="form-control"
placeholder="请输入图书名称"
name="bookname"
/>
</div>
<!-- 作者 -->
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text">作者</span>
</div>
<input
type="text"
class="form-control"
placeholder="请输入作者名字"
name="author"
/>
</div>
<!-- 出版社 -->
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text">出版社</span>
</div>
<input
type="text"
class="form-control"
placeholder="请输入出版社名称"
name="publisher"
/>
</div>
<!-- 添加按钮 -->
<button class="btn btn-dark" type="submit">添加</button>
</form>
</div>
</div>
</div>
</div>
<!-- loading 区域 -->
<div class="loading-box">
<div class="spinner-border m-5 text-primary" role="status">
<span class="sr-only">Loading...</span>
</div>
</div>
<!-- 导入axios -->
<script src="./lib/axios.js"></script>
<script>
//设置axios基地址 : 所有的请求 默认添加前面的 http://域名
axios.defaults.baseURL = 'http://www.liulongbin.top:3006'
//函数(1) : 封装请求所有图书列表的函数
function renderBookList() {
axios
.get("/api/getbooks")
.then(({ data: res }) => {
console.log(res)
document.querySelector("tbody").innerHTML = res.data
.map(item => {
return `<tr>
<th scope="row">${item.id}</th>
<td>${item.bookname}</td>
<td>${item.author}</td>
<td>${item.publisher}</td>
<td>
<button type="button" data-id="${item.id}" class="btn delete btn-link btn-sm">删除</button>
</td>
</tr>`
})
.join("")
})
}
//函数(2) : 封装获取表单参数的函数
function getFormData() {
/* 表单name属性作用: 告诉服务器,这个表单的数据是什么
开发中,表单的name属性一般会根据接口文档来设置。
接口文档参数是什么,表单name就写什么
*/
let inputList = document.querySelectorAll("#addForm input")
//声明空对象
let obj = {}
//遍历数组
inputList.forEach(item => {
// 表单name : 接口参数名 表单value : 接口参数值
obj[item.name] = item.value
})
//返回对象
return obj
}
//axios拦截器使用
// 添加请求拦截器
axios.interceptors.request.use(
function(config) {
// 可以操作这次请求了
// console.log(config); // ajax请求参数;
// 展示 loading 效果
document.querySelector(".loading-box").style.display = "block"
// 返回固定数据
return config
},
function(error) {
// 对请求错误做些什么 , 下面这句话,固定语法;
return Promise.reject(error)
}
)
// 添加响应拦截器
axios.interceptors.response.use(
function(response) {
//服务器响应的数据
// console.log(response);
// 隐藏 loading 效果
document.querySelector(".loading-box").style.display = "none"
// 把服务器响应的数据返回给axios的then方法
return response
},
function(error) {
// 隐藏 loading 效果
$(".loading-box").hide() // 失败了,也要隐藏 loadding 效果;
// 对响应错误做点什么
return Promise.reject(error)
}
)
// 1.页面加载后,要初始化图书列表; 函数不调用不执行;
renderBookList()
//2. 添加按钮点击
document.querySelector("#addForm").onsubmit = function(e) {
// (1)阻止表单提交
e.preventDefault()
// (2)获取表单的参数
let data = getFormData()
// (3)非空判断
if (Object.values(data).some(item => item == "")) {
alert("输入框不能为空")
return
}
// (4)发送ajax
axios({
url: "/api/addbook",
method: "post",
data
}).then(res => {
//成功回调 201 : 成功 其他状态码 : 失败
if (res.data.status != 201) {
return alert(res.data.msg)
}
// 成功提示
alert(res.data.msg)
// 重新初始化图书列表
renderBookList()
// 重置form表单
document.querySelector("#addForm").reset()
})
}
//3.使用事件委托给删除按钮注册事件
/* 事件委托: 给父元素注册事件,委托子元素处理 */
document.querySelector(".table>tbody").onclick = function(e) {
//判断是否是委托的子元素
//删除按钮需要增加两个属性 (1)类名用于判断 (2)自定义属性存储id
if (e.target.classList.contains("delete")) {
let id = e.target.getAttribute("data-id")
axios({
url: "/api/delbook",
method: "get",
params: { id }
}).then(res => {
//成功回调
if (res.data.status != 200) {
// 失败提示
return alert(res.data.msg)
}
// 成功提示
alert(res.data.msg)
// 重新渲染图书列表
renderBookList()
})
}
}
</script>
</body>
</html>
1.1-axios拦截器使用
/* axios拦截器工作流程
-
axios 发起请求
-
执行 请求拦截器 : 添加ajax发送请求之前的操作
-
服务器 接收、处理、响应 请求
-
执行 响应拦截器 : 添加服务器响应之后的操作
-
axios 接收响应(执行then方法)
*/
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link rel="stylesheet" href="./lib/bootstrap-v4.6.0.css" />
<style>
body {
padding: 20px;
}
.loading-box {
position: fixed;
top: 30%;
left: 50%;
transform: translateX(-50%);
background-color: rgba(0, 0, 0, 0.1);
border-radius: 10px;
box-shadow: 1px 1px 3px #efefef;
display: none;
}
</style>
</head>
<body>
<!-- 按钮 -->
<button class="btn btn-primary" id="btn1">请求1</button>
<button class="btn btn-info" id="btn2">请求2</button>
<button class="btn btn-warning" id="btn3">请求3</button>
<!-- loading 区域 -->
<div class="loading-box">
<div class="spinner-border m-5 text-primary" role="status">
<span class="sr-only">Loading...</span>
</div>
</div>
<script src="./lib/axios.js"></script>
<script>
// 添加请求拦截器
axios.interceptors.request.use(
function(config) {
// 可以操作这次请求了
// console.log(config); // ajax请求参数;
// 展示 loading 效果
document.querySelector(".loading-box").style.display = "block"
// 返回固定数据
return config
},
function(error) {
// 对请求错误做些什么 , 下面这句话,固定语法;
return Promise.reject(error)
}
)
// 添加响应拦截器
axios.interceptors.response.use(
function(response) {
//服务器响应的数据
// console.log(response);
// 隐藏 loading 效果
document.querySelector(".loading-box").style.display = "none"
// 把服务器响应的数据返回给axios的then方法
return response
},
function(error) {
// 隐藏 loading 效果
$(".loading-box").hide() // 失败了,也要隐藏 loadding 效果;
// 对响应错误做点什么
return Promise.reject(error)
}
)
//按钮1
document.querySelector("#btn1").onclick = function() {
axios({
url: "http://www.liulongbin.top:3009/api/news",
method: "get"
}).then(res => {
//成功回调
console.log(res)
})
}
//按钮2
document.querySelector("#btn2").onclick = function() {
axios({
url: "https://autumnfish.cn/fruitApi/fruits",
method: "get"
}).then(res => {
//成功回调
console.log(res)
})
}
//按钮3
document.querySelector("#btn3").onclick = function() {
axios({
url: "http://www.liulongbin.top:3009/api/login",
method: "post",
data:{ username:'admin',password:'123456'}
}).then(res => {
//成功回调
console.log(res)
})
}
</script>
</body>
</html>
1.2-axios基地址
//设置axios基地址 : 所有的请求 默认添加前面的 http://域名
axios.defaults.baseURL = 'www.liulongbin.top:3006'
02-ajax补充知识点
1.1-onreadstatechange事件(了解)
官网文档传送门:https://www.runoob.com/ajax/ajax-xmlhttprequest-onreadystatechange.html
-
onload事件 : 接收服务器响应的数(一次请求,只会执行一次)
-
onreadystatechang事件 : 作用与onload事件一致(一次请求,会执行多次)
面试点: XMLHttpRequest对象的状态码 (xhr.readyState)
0: 请求未建立 (创建了xhr对象,但是还没调用open)
1: 服务器连接已建立
-
请求已接收 (send之后,服务器已经接收了请求)
-
请求处理中
-
请求已完成,且响应已就绪 ( 4状态码等同于onload事件 )
//(1).实例化ajax对象
let xhr = new XMLHttpRequest()
console.log(xhr.readyState)
//(2).设置请求方法和地址
xhr.open("post", "http://www.liulongbin.top:3009/api/login")
//(3).设置请求头(post请求才需要设置)
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded")
console.log(xhr.readyState)
//(4).发送请求 : 参数格式 'key=value'
xhr.send("username=admin&password=123456")
//(5).注册回调函数
//a. onload 是新式浏览器才支持的
//b. 如果要兼容更早的浏览器,可以使用 onreadystatechange
//c. onreadystatechange触发时机 : xhr.readState状态变化
// xhr.onload = function() {};
xhr.onreadystatechange = function() {
console.log(xhr.readyState)
//onreadystatechange会触发多次,一般需要判断xhr.readState == 4 才获取响应数据
if (xhr.readyState == 4) {
console.log(xhr.responseText)
}
}
1.2-Ajax组成部分了解(了解)
Ajax(阿贾克斯):全称 Asynchronous Javascript And XML(异步的js与xml)
- 说人话: 用js发送异步的网络请求
- A : Asynchronous 异步
-
- 同步 : 指的是代码按照从上往下顺序执行
- 异步 : 代码不会立即执行,而是要等一会儿执行
-
- 目前我们学过的ECMAScript只有两个语法是异步的: 定时器 与 ajax
- DOM事件也是属于异步的,但是这个是属于DOM的执行机制。所以一般在讨论js同步和异步的时候,主要以js为主,DOM一般不讨论。
- J:Javascript
- A :And
- X : XML 与 XMLHttpRequest
-
- XML : 解决跨平台数据传输。
-
- 在JSON没有出来以前, 网络传输主要以XML格式数据为主。 后来JSON问世,逐渐取代XML。 但是由于ajax技术出来的比json早,因此xml这个称呼一直保留至今
1.3-get请求与post请求区别(掌握)
- 1.传参方式不同
-
- get在url后面拼接(请求行)
- post在请求体传参
- 2.大小限制不同
-
- get有大小限制,不同浏览器大小限制不同。 一般2-5 MB
- post没有大小限制
- 3.安全性不同
-
- get参数直接暴露在url,不安全(一般查询类数据都是get)
- post参数在请求体中,更加安全(一般登录注册必须是post)
- 4.传输速度不同
-
- get传输速度快
- post传输速度慢
1.4-全局更新与局部更新方法了解(掌握)
实际开发中,前端无权决定请求方法,只需要根据后台接口文档来就可以了
- put和pacth区别
-
- 全局更新 : put
- 局部更新: patch
今日接口文档
图书管理基地址:http://www.liulongbin.top:3006
1 图书列表
- 接口URL: /api/getbooks
- 调用方式: GET
- 参数格式:
2 添加图书
- 接口URL: /api/addbook
- 调用方式: POST
- 参数格式:
3 删除图书
- 接口URL: /api/delbook
- 调用方式: GET
- 参数格式: