我们的学习过程中需要参考一篇接口文档
点击进入接口文档
GET 提交表单数据有个缺陷,就是我们用户的密码会直接显示在地址里
<!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="./css/bootstrap.min.css" />
<link rel="stylesheet" href="./css/login.css" />
</head>
<body>
<div class="login-box">
<form>
<div class="form-group mb-3">
<label for="username">Account</label>
<!-- 账号 -->
<input type="text" class="form-control" name="username" id="username" autocomplete="off" />
<small id="emailHelp" class="form-text text-muted">The available account is <strong>admin</strong></small>
</div>
<div class="form-group mb-3">
<!-- 密码 -->
<label for="password">Password</label>
<input type="password" class="form-control" name="password" id="password" />
<small id="emailHelp" class="form-text text-muted">The available password is <strong>123456</strong></small>
</div>
<button type="submit" class="btn btn-primary" id="btnLogin">Submit</button>
</form>
</div>
<script src="./lib/axios.js"></script>
<script>
// 请求方式 POST
// 地址 http://www.liulongbin.top:3009/api/login
// 参数: username 用户名 password 密码
// 使用Ajax提交表单数据的步骤
// 1. 监听表单的 submit 提交事件 ==> 当表单提交的时候会触发submit事件
// 2. 阻止默认提交行为 ==> e.preventDefault()
// 3. 基于 axios 发起请求
// 4. 指定请求方式、请求地址、指定请求体数据
// 1.
document.querySelector('form').addEventListener('submit', function (e) {
// 2. 阻止默认的提交行为
e.preventDefault()
// 提交的时候,来获取
let username = document.querySelector('#username').value
let password = document.querySelector('#password').value
// 3.
axios({
method: 'post',
url: 'http://www.liulongbin.top:3009/api/login',
data: {
username,
password
}
}).then(({ data: res }) => {
console.log(res)
})
})
</script>
</body>
</html>
<!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="./css/bootstrap.min.css" />
<link rel="stylesheet" href="./css/login.css" />
</head>
<body>
<div class="login-box">
<form>
<div class="form-group mb-3">
<label for="username">Account</label>
<!-- 账号 -->
<input type="text" class="form-control" name="username" id="username" autocomplete="off" />
<small id="emailHelp" class="form-text text-muted">The available account is <strong>admin</strong></small>
</div>
<div class="form-group mb-3">
<!-- 密码 -->
<label for="password">Password</label>
<input type="password" class="form-control" name="password" id="password" />
<small id="emailHelp" class="form-text text-muted">The available password is <strong>123456</strong></small>
</div>
<button type="submit" class="btn btn-primary" id="btnLogin">Submit</button>
</form>
</div>
<script src="./lib/axios.js"></script>
<script src="./lib/form-serialize.js"></script>
<script>
// 请求方式 POST
// 地址 http://www.liulongbin.top:3009/api/login
// 参数: username 用户名 password 密码
// 使用Ajax提交表单数据的步骤
// 1. 监听表单的 submit 提交事件
// 2. 阻止默认提交行为
// 3. 基于 axios 发起请求
// 4. 指定请求方式、请求地址、指定请求体数据
// 引入form-serialize插件,提供 serialize 函数,从而获取表单数据
// serialize(form表单的DOM对象)
// let form = document.querySelector('form')
// 都可以获取到表单数据
// serialize(form) // 'username=admin&password=123456'
// serialize(form, { hash: true }) // js对象 {username: 'admin', password: '123456'}
document.querySelector('form').addEventListener('submit', function (e) {
// 2. 阻止默认的提交行为
e.preventDefault()
// 提交的时候,serialize插件来获取表单数据
let data = serialize(this, { // this 指向了事件源
// {} 配置对象 hash 配置,可以将 收集到的表单数据是个js对象格式
hash: true
})
console.log(data)
// 3.
axios({
method: 'post',
url: 'http://www.liulongbin.top:3009/api/login',
// 这个data不能省,否则没有提交数据给服务器
data
}).then(({ data: res }) => {
console.log(res)
})
})
</script>
</body>
</html>
上传到服务器后可以自行检查一下,复制根路径http://www.liulongbin.top:3006
加上浏览器终端中的 url 地址:/uploads/1661265302836_7e9cb873e5564c9d9cc5dd66a59fd101.jpg
到浏览器进行访问,就可以看到我们刚上传的图片。
了解清楚如何得到这个图片的地址后,后面实现头像的更换就简单了
随后我们发现一个问题,就是如果点击按钮打开选择头像文件弹窗,但是我们不选择头像直接点击打开的话,会报错:
业务响应码为 502,代表它为 502 时我们并没有上传头像,所以要对代码进行优化。
最终代码:
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>案例-头像上传title>
<link rel="stylesheet" href="./lib/bootstrap-v4.6.0.css">
<style>
.thumb-box {
text-align: center;
margin-top: 50px;
}
.thumb {
width: 250px;
height: 250px;
object-fit: cover;
border-radius: 50%;
}
style>
head>
<body>
<div class="thumb-box">
<img src="./images/cover.jpg" class="img-thumbnail thumb" alt="">
<div class="mt-2">
<input type="file" id="iptFile" accept="image/*" style="display: block;">
<button class="btn btn-primary" id="btnChoose">选择 & 上传图片button>
div>
div>
<script src="./lib/axios.js">script>
<script>
// 在线接口文档:https://www.showdoc.com.cn/ajaxapi/3754974183518732
let btnChoose = document.querySelector('#btnChoose')
let iptFile = document.querySelector('#iptFile')
let img = document.querySelector('.thumb')
// 功能
// 1. 点击按钮,弹出文件选择框 ==> 模拟点击文件选择器,从而实现弹框来选择文件
// 给按钮添加点击事件,按钮被点击后模拟点击一次 iptFile 实现 选择弹窗
btnChoose.addEventListener('click',function(){
iptFile.click()
})
// 2. 实现文件的上传功能
// FormData 存文件 === axios 发请求(看接口文档)
iptFile.addEventListener('change', function(){
console.log(this.files[0])
// 当 this.files[0] 是 undefined 时,表示用户没有选中文件,以下代码不执行
if(!this.files[0]){ // undefined是false,!false 就是 ture。if(true)则执行return 终止后续代码的执行
alert('未选择任何文件,请重新上传')
return
}
let fd = new FormData()
// 接口文档要求了,参数名得是 avatar
fd.append('avatar', this.files[0])
// 上传到服务器
axios({
method: 'post',
// 请求的 url 地址一定要从接口文档中去复制
url: 'http://www.liulongbin.top:3006/api/upload/avatar',
// data 的值是 fd(把FormData存的数据给发送到服务器上)
data: fd
}).then(({data: res}) => {
console.log(res)
console.log(res.url)
// 根路径 加 请求回来的 url 地址 就是 我们发送到服务器上那张头像的地址
// 这里给 img.src 更改地址实现更换头像
img.src = `http://www.liulongbin.top:3006${res.url}`
})
})
script>
body>
html>
有一个事情需要强调一下,我们写 Ajax 代码不是前端说了算,是后端说了算,看他们给的接口文档。
<!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"> -->
<link rel="stylesheet" href="../lib/bootstrap-v4.6.0.css">
<style>
:root {
font-size: 15px;
}
body {
padding-top: 15px;
}
</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 btn-del">删除</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>
<script src="../lib/form-serialize.js"></script>
<script src="../lib/axios.js"></script>
<script>
// 功能
// 1. 渲染数据
// 2. 添加数据
// 3. 删除数据
let tbody = document.querySelector('tbody')
let addForm = document.querySelector('#addForm')
// 1. 渲染数据
axios({
method: 'get',
url: 'http://www.liulongbin.top:3006/api/getbooks',
}).then(({data: res}) => {
console.log(res)
console.log(res.data)
// 为什么是 status? 看接口文档,接口文档要求了这个属性
if(res.status !== 200){
alert(res.msg)
} else {
// 1. 渲染数据
tbody.innerHTML = res.data.map(item =>
`
${item.id}
${item.bookname}
${item.author}
${item.publisher}
`
).join('')
}
})
</script>
</body>
</html>
这里有一个细节,当我们点击提交按钮,成功添加图书之后,应该要把表单里的内容清空,方便我们后续的添加操作。传统做法
是先获取输入框对象,然后给它的value值设置为空。但是我们这个表单有三个输入框,一个一个进行设置空操作非常麻烦。
我们有一个更简洁的做法
:reset() 方法
<!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"> -->
<link rel="stylesheet" href="../lib/bootstrap-v4.6.0.css">
<style>
:root {
font-size: 15px;
}
body {
padding-top: 15px;
}
</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 btn-del">删除</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>
<script src="../lib/form-serialize.js"></script>
<script src="../lib/axios.js"></script>
<script>
// 功能
// 1. 渲染数据
// 2. 添加数据
// 3. 删除数据
let tbody = document.querySelector('tbody')
let addForm = document.querySelector('#addForm')
// 功能1. 渲染数据
// 对代码进行封装
function render() {
axios({
method: 'get',
url: 'http://www.liulongbin.top:3006/api/getbooks',
}).then(({data: res}) => {
console.log(res)
console.log(res.data)
if(res.status !== 200){
alert(res.msg)
} else {
// 1. 渲染数据
tbody.innerHTML = res.data.map(item =>
`
${item.id}
${item.bookname}
${item.author}
${item.publisher}
`
).join('')
}
})
}
render()
// 功能2. 添加数据
// 步骤
// 1. 监听form的submit事件
// 2. 阻止其默认行为
// 3. 表单数据收集到 ==> serialize()
// 4. axios 发送请求
addForm.addEventListener('submit',function(e){
// 阻止默认的提交
e.preventDefault()
// let data = serialize(this, {hash: true}) // 写明 hash: true 的话得到的就是 js对象
let data = serialize(this) // 不写明 hash: true 的话得到的就是 键值对
console.log(data)
axios({
url: 'http://www.liulongbin.top:3006/api/addbook',
method: 'post',
data
}).then(({data: res}) => {
console.log(res)
// status 主要会出现 502这个情况(重复添加信息),提示 不允许重复添加。
// 确定 201 这个响应码 是输出 console.log(res) 在 res 中看到的
if(res.status !== 201) {
// 添加失败了
alert(res.msg)
} else {
// 添加成功了
// 1.重新渲染所有图书展示到 tbody 中
render()
// 2.清空输入栏内的内容(清空表单)
addForm.reset() // 用 reset() 方法重置表单
}
})
})
</script>
</body>
</html>
自定义属性 data-id 来帮助我们 实现删除功能
标签上使用 data- 自定义属性
js 中使用 dataset 属性操作
<!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"> -->
<link rel="stylesheet" href="../lib/bootstrap-v4.6.0.css">
<style>
:root {
font-size: 15px;
}
body {
padding-top: 15px;
}
</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 btn-del">删除</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>
<script src="../lib/form-serialize.js"></script>
<script src="../lib/axios.js"></script>
<script>
// 功能
// 1. 渲染数据
// 2. 添加数据
// 3. 删除数据
let tbody = document.querySelector('tbody')
let addForm = document.querySelector('#addForm')
// 功能1. 渲染数据
// 对代码进行封装
function render() {
axios({
method: 'get',
url: 'http://www.liulongbin.top:3006/api/getbooks',
}).then(({data: res}) => {
console.log(res)
console.log(res.data)
if(res.status !== 200){
alert(res.msg)
} else {
// 1. 渲染数据
tbody.innerHTML = res.data.map(item =>
`
${item.id}
${item.bookname}
${item.author}
${item.publisher}
`
).join('')
}
})
}
render()
// 功能2. 添加数据
// 步骤
// 1. 监听form的submit事件
// 2. 阻止其默认行为
// 3. 表单数据收集到 ==> serialize()
// 4. axios 发送请求
addForm.addEventListener('submit',function(e){
// 阻止默认的提交
e.preventDefault()
// let data = serialize(this, {hash: true}) // 写明 hash: true 的话得到的就是 js对象
let data = serialize(this) // 不写明 hash: true 的话得到的就是 键值对
console.log(data)
axios({
url: 'http://www.liulongbin.top:3006/api/addbook',
method: 'post',
data
}).then(({data: res}) => {
console.log(res)
// status 主要会出现 502这个情况(重复添加信息),提示 不允许重复添加。
// 确定 201 这个响应码 是输出 console.log(res) 在 res 中看到的
if(res.status !== 201) {
// 添加失败了
alert(res.msg)
} else {
// 添加成功了
// 1.重新渲染所有图书展示到 tbody 中
render()
// 2.清空输入栏内的内容(清空表单)
addForm.reset() // 用 reset() 方法重置表单
}
})
})
// 功能3:删除图书
// 给删除按钮注册click ==> 采取事件委托做法(删除按钮动态创建)
// 做法: 把事件委托给父元素或祖先元素
// 原理:事件冒泡
tbody.addEventListener('click', function(e){
console.log(e.target)
// 判断点击的对象 是否为 删除按钮
// if(e.target.tagName === 'BUTTON'){}
// 更加具体些,因为我们可能不止有删除按钮,可能还有修改编辑按钮。
if(e.target.classList.contains('btn-del')){ // contains('btn-del') 判断是否有这个类名
// if成立,说明有 btn-del 这个类名,点击的是删除按钮
// 点击删除按钮的时候,需要将自定义属性的值取出来
let id = e.target.dataset.id
console.log(id)
// 发送 ajax 请求,告诉服务器,需要删除哪一本书
axios({
method: 'delete',
url: 'http://www.liulongbin.top:3006/api/delbook',
params: {
// 需要删除的图书id ==> 在删除按钮上使用自定义属性存图书的id
id
}
}).then(({data: { status, msg }}) => {
// console.log(res)
if(status !== 200) {
// 删除失败
alert(msg)
} else {
// 删除成功 重新渲染才能看到图书被删除的效果
render()
}
})
}
})
</script>
</body>
</html>
当删除图书时遇到一个不清楚的报错,这样解决(学会在报文中发现问题)
相当于点击删除按钮的时候直接取 id,但是我们根本没在按钮上自定义 id 属性,所以无法获取。
发送 get 请求,不带参数:
发送 get 请求,带参数:
知识点补充:
从用法来说
axios.delete() 同 axios.get()
axios.put() 和 axios.patch() 同 axios.post()
发送 post 请求,不带参数:
发送 post 请求,带参数:
我们这个参数a 参数b 的值给的很随意,为什么呢?因为接口文档中明确表明: 我们设置的参数是自定义,按需提供的。
timeout 的意义在于,当用户的网速太慢,网页请求时间超过我们设置的超时时间就报错停止,不要让用户傻傻等待。
学习玩 axios 的请求别名和 全局配置请求根路径后,我们可以对之前做的图书案例的代码进行简化。