目录
GET 请求:用来获取数据,数据通过 URL 的参数(例如 ?creator=老张)传给服务器,一般用于“读”操作,比如获取书籍列表、文章内容、用户信息等
POST 请求:用来提交数据(如:新增、注册、提交表单等),数据通过 请求体(body) 传递,不会暴露在地址栏里,一般用于“写”操作,比如:新增图书、用户注册、发表评论等。
本节素材已上传至Gitee:https://gitee.com/liu-yihao-hhh/ajax_studyhttps://gitee.com/liu-yihao-hhh/ajax_study
一、BootStrap 弹窗
1. 通过属性的方式来控制弹框的显示或隐藏
- 引入bootstrap.css 和 bootstrap.js
<!-- 引入bootstrap.css -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- 引入bootstrap.js -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/bootstrap.min.js"></script>
- 准备弹框标签,确认结构
Bootstrap(modal):Modal · Bootstrap v5.3
<!--
弹框标签
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>
- 给点击按钮,通过自定义属性,控制弹框的显示和隐藏
class="btn btn-primary" data-bs-toggle="modal" 时bootstrap js库给我们提供的
由于一个html可能存在多个弹框,所以我们要给该弹框绑定自定义属性data-bs-target
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target=".my-box">
显示弹框
</button>
关闭弹窗属性
data-bs-dismiss="modal"
2. js控制弹窗的显示和隐藏
就是简单的js对象方法调用 学了vue之后 这些都是小菜j
// 创建弹框对象
const modalDom = document.querySelector('.name-box')
const modal = new bootstrap.Modal(modalDom)
// 编辑姓名-> 点击 -> 弹框显示
document.querySelector('.btn-primary').addEventListener('click', () => {
// 2. 显示弹框
modal.show()
})
document.querySelector('.save-btn').addEventListener('click', () => {
// 2. 点击保存隐藏弹框
modal.hide()
})
总结:
- 如果再关闭弹窗的时候,是想要直接关闭,就可以用属性的方式更加方便一点;
- 如果是再关闭之前有一些逻辑要进行执行(比如:返回数据,获取数据)就是js方法进行显示或隐藏更好;
本节素材已上传至Gitee:https://gitee.com/liu-yihao-hhh/ajax_studyhttps://gitee.com/liu-yihao-hhh/ajax_study
二、 图书管理 - 渲染列表
数据请求+渲染:
const demo = async () => {
// 1.1 获取数据
const reperson = await axios.get('http://hmajax.itheima.net/api/books', {
params: {
// 外号: 获取对应数据
creator: 'hha'
}
})
const bookList = reperson.data.data
console.log(bookList)
// 1.2 渲染数据
const htmlStr = bookList.map((item, index) => {
return `
<tr>
<td>${index}</td>
<td>${item.bookname}</td>
<td>${item.author}</td>
<td>${item.publisher}</td>
<td>
<span class="del">删除</span>
<span class="edit">编辑</span>
</td>
</tr>
`
}).join('')
console.log(htmlStr)
document.querySelector('.list').innerHTML = htmlStr
}
demo()
1. params
是用来给服务器接口 传递查询参数 的
它对应的是 HTTP GET 请求里的 URL 查询字符串(query string),形如:
http://hmajax.itheima.net/api/books?creator=老张
axios
里你写:params: { creator: '老张' }
它帮你自动把这个对象转成
?creator=老张
拼接到 URL 后面。
2. 为什么你以前读 JSON 文件不需要?
你以前可能是直接访问本地静态的
.json
文件,比如:axios.get('/data/books.json')
这是直接读静态文件,不需要任何额外参数,服务器就直接给你完整数据。
3. 但这里请求的是 服务器接口
这个接口会根据你传递的参数(如
creator
)返回不同的数据;如果你不给它参数,服务器会拒绝,返回 400 错;
所以你必须通过
params
明确告诉服务器你要查谁的书。
4. 总结一句话:
params
就是告诉服务器“我请求的这个接口,请根据这些条件帮我筛选数据”,而你直接读 JSON 文件则不需要,因为那是静态资源,没筛选。
额外举个通俗例子
你去餐厅点餐:
JSON 文件相当于菜单,你拿菜单看内容,不需要额外说明;
接口请求相当于你点菜,你得告诉服务员“我要一份宫保鸡丁”,这“宫保鸡丁”就是参数。
点击添加要弹出bootStrap-modal弹窗!
给当前按钮添加一个自定义属性,绑定后进行显示。
点击取消可以直接隐藏弹框,但是点击保存就是要将填入的数据进行存入再后台,然后再次往页面进行渲染!所以点击保存,要走js操作流程!!!
新增图书
// 创建一个弹框对象
const addModalDom = document.querySelector('.add-modal')
const addModal = new bootstrap.Modal(addModalDom)
document.querySelector('.add-btn').addEventListener('click', async () => {
// 收集表单,并提交到服务器
const addForm = document.querySelector('.add-form')
// serialize插件 它能把 HTML 表单序列化成对象或字符串,非常方便。
// hash:true -> "name=小智&age=20" 转换成 { name: "小智", age: "20" }
// empty: true -> 加了 empty: true,即使表单项为空,也会把该键以空字符串 "" 的形式序列化进去。
const bookObj = serialize(addForm, { hash: true, empty: true })
console.log(bookObj) // {bookname: '哈哈', author: '流哈哈', publisher: '武汉传媒学院'}
// 提交到服务器
// axios({
// url: 'http://hmajax.itheima.net/api/books',
// method: 'POST',
// data: {
// ...bookObj,
// creator: '老张'
// }
// }).then(result => {
// console.log(result)
// // 2.3 添加成功后, 重新请求并渲染图书列表
// getBooksList()
// })
const response = await axios.post('http://hmajax.itheima.net/api/books', {
...bookObj,
creator: 'hha'
})
console.log(response)
await getBooksList()
addModal.hide()
})
里面有两点:
post是什么?和get有什么区别?
GET
请求:用来获取数据,数据通过 URL 的参数(例如 ?creator=老张
)传给服务器,一般用于“读”操作,比如获取书籍列表、文章内容、用户信息等
POST
请求:用来提交数据(如:新增、注册、提交表单等),数据通过 请求体(body) 传递,不会暴露在地址栏里,一般用于“写”操作,比如:新增图书、用户注册、发表评论等。
二者都要传递参数,creator:'hha'
axios.post(url, data)
,第二个参数是请求体data
,直接把对象发给服务器。axios.get(url, config)
,第二个参数是配置对象,不能直接放数据,参数必须写在config.params
里。
因为 HTTP 协议规定:GET 请求没有请求体,数据只能放 URL 参数里,这也是 RESTful 设计规范的基本要求。
creator相当于post中是谁进行提交到的,get中是要获取谁提交的数据,所以get和post二者的creator要一致!!!
收集表单插件 —— serialize插件
// serialize插件 它能把 HTML 表单序列化成对象或字符串,非常方便。
// hash:true -> "name=小智&age=20" 转换成 { name: "小智", age: "20" }
// empty: true -> 加了 empty: true,即使表单项为空,也会把该键以空字符串 "" 的形式序列化进去。
const bookObj = serialize(addForm, { hash: true, empty: true })
console.log(bookObj) // {bookname: '哈哈', author: '流哈哈', publisher: '武汉传媒学院'}
删除图书
// 删除图书
// 3.1 删除元素 -> (事件委托)
document.querySelector('.list').addEventListener('click', e => {
// 获取触发事件目标元素
// console.log(e.target)
// 判断点击的是删除元素 contains常用来判断一个元素的某个 class 类名 是否存在。
if (e.target.classList.contains('del')) {
console.log('点击删除元素')
// 获取图书的id
// parentNode 上面一个父节点
// closest('.') 指定父节点
const id = e.target.dataset.id
const demo = async () => {
await axios.delete(`http://hmajax.itheima.net/api/books/${id}`)
await getBooksList()
}
demo()
}
})
这里我真的蠢死了,我开始没有给html删除标签打上data-id,然后我就想通过过滤来覆盖服务器里面的数组,结果我没有清空,内容就越来越多。可是清空就必须要得到id,我就没办法,我还是又axios.get()来得到一遍数据,转换成数组来得到我点击的是哪个元素来得到里面的id,这里真的蠢死了,其实只要给删除按钮打上data-id就可以了直接获得id了。
删除语法:是通过路径后面 + id的形式来进行删除
const demo = async () => {
await axios.delete(`http://hmajax.itheima.net/api/books/${id}`)
await getBooksList()
}
demo()
注意:
contains是类名的操作方法,所以都是在classList里面存在的~ 判断是否存在当前类名
e.target.classList.contains('del')
编辑图书
我最开始直接想要获取点击编辑按钮的DOM元素,可是失败了
document.querySelector('.edit').addEventListener('click', () => {
就是因为edit是存在多个页面标签,存在多个就需要用到 querySelectorAll,所以如果直接获取edit元素就要用querySelectorAll.forEach()来进行遍历,当前点击的是哪个edit元素
那么就还是对父节点list进行查找e.target.classList.contains('edit')当前的点击元素比较方便
将浏览器数据进行渲染到编辑框中
首先获取浏览器的数据,对他进行数据解构,利用Object.keys(),能将bookObj的键存成一个数组,在对该数组进行遍历来修改DOM元素节点的值,这里要注意遍历的时候,key是每个元素,点语法
.key
是访问字面量属性名,方括号语法[key]
才是访问变量代表的属性名。
// axios来获取详细数据
const response = await axios.get(`http://hmajax.itheima.net/api/books/${theId}`)
const bookObj = response.data.data
// document.querySelector('.edit-form .bookname').value = bookObj.bookname
// document.querySelector('.edit-form .author').value = bookObj.author
// document.querySelector('.edit-form .publisher').value = bookObj.publisher
// 数据对象“属性” 和 标签“类名”一致
// 遍历数据对象, 使用属性去获得对应的标签, 快速赋值
const keys = Object.keys(bookObj) // ['id', 'bookname', 'author', 'publisher']
keys.forEach(key => {
// 通过变量名 动态 访问对象属性,必须用中括号 [],不能用点语法。
document.querySelector(`.edit-form .${key}`).value = bookObj[key]
})
点击修改按钮
要进行数据修改,就是对该页面修改完成后,重新获取form表单数据,然后重新返回服务器,然后再次渲染页面,这里就用上了axios put提交信息语法,直接在链接后面跟上data数据{},同时要跟上creator:'hha'来进行修改者的输入
// 进行解构
const { id, bookname, author, publisher } = serialize(editForm, { hash: true, empty: true })
const response = await axios.put(`http://hmajax.itheima.net/api/books/${id}`, {
bookname,
author,
publisher,
creator: 'hha'
})
// 修改成功 重新渲染列表
getBooksList()
// 目标4 编辑图书
const editDom = document.querySelector('.edit-modal')
const editModal = new bootstrap.Modal(editDom)
document.querySelector('.list').addEventListener('click', async e => {
// 为什么非要这么写呢 就是因为页面存在多个edit标签, 你就必须要用querySelectorAll.forEach()来进行遍历, 那么这种查找就不需要进行遍历
if (e.target.classList.contains('edit')) {
// console.log('一定是编辑')
editModal.show()
// console.log('e', e.target)
// 点击编辑就要拿到测试的id 原生js找任意兄弟节点属性
const theId = e.target.parentNode.querySelector('.del').dataset.id
console.log(theId)
// axios来获取详细数据
const response = await axios.get(`http://hmajax.itheima.net/api/books/${theId}`)
const bookObj = response.data.data
// document.querySelector('.edit-form .bookname').value = bookObj.bookname
// document.querySelector('.edit-form .author').value = bookObj.author
// document.querySelector('.edit-form .publisher').value = bookObj.publisher
// 数据对象“属性” 和 标签“类名”一致
// 遍历数据对象, 使用属性去获得对应的标签, 快速赋值
const keys = Object.keys(bookObj) // ['id', 'bookname', 'author', 'publisher']
keys.forEach(key => {
// 通过变量名 动态 访问对象属性,必须用中括号 [],不能用点语法。
document.querySelector(`.edit-form .${key}`).value = bookObj[key]
})
}
})
// 点击修改按钮
document.querySelector('.edit-btn').addEventListener('click', async () => {
// 提交保存修改 并刷新列表
const editForm = document.querySelector('.edit-form')
// 进行解构
const { id, bookname, author, publisher } = serialize(editForm, { hash: true, empty: true })
const response = await axios.put(`http://hmajax.itheima.net/api/books/${id}`, {
bookname,
author,
publisher,
creator: 'hha'
})
// 修改成功 重新渲染列表
getBooksList()
editModal.hide()
})
整体来说,这一节就是学习了axios与服务器交互的各种语法
方法 | 用途 | 请求语法 | 请求数据位置 | 示例 |
---|---|---|---|---|
get |
获取数据(查询) | axios.get(url, { params }) |
查询参数在 URL 中(params 会拼接) |
axios.get('/api/books', { params: { page: 1 } }) |
post |
新增数据 | axios.post(url, data) |
数据在请求体中(body ) |
axios.post('/api/books', { title: '书名' }) |
put |
修改全部字段 | axios.put(url, data) |
数据在请求体中(更新整条记录) | axios.put('/api/books/1', { title: '新书名', author: '作者' }) |
patch |
修改部分字段 | axios.patch(url, data) |
数据在请求体中(只传改动字段) | axios.patch('/api/books/1', { author: '新作者' }) |
delete |
删除数据 | axios.delete(url) 或 axios.delete(url, { data }) |
通常 ID 写在 URL,复杂情况可带 body | axios.delete('/api/books/1') |
本节素材已上传至Gitee:https://gitee.com/liu-yihao-hhh/ajax_studyhttps://gitee.com/liu-yihao-hhh/ajax_study
三、图片上传
- 获取图片文件对象
- 使用FormData携带图片文件
const fd = new FormData() fd.append(参数名, 值)
- 提交表单数据到服务器, 使用图片url网址
FormData
是浏览器内置的一个对象,用于构造表单数据,可以方便地用于POST
请求中,特别是文件上传。
往服务器里面提交图片为什么非要是FormData浏览器内置对象呢?
因为普通的 JSON 对象是不能包含文件(二进制数据)的。如果你用这种方式写:
const data = {
img: e.target.files[0]
}
然后发送 JSON 请求:会失败,服务器接收不到上传的图片。
axios.post(url, data)
<!-- 文件选择元素 -->
<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', async e => {
// 1. 获取图片文件
console.log(e.target.files[0])
// 2. 使用FormData 携带图片文件
const fd = new FormData()
fd.append('img', e.target.files[0])
// 3. 提交到服务器 获取图片url网址使用
const response = await axios.post('http://hmajax.itheima.net/api/uploadimg', fd)
console.log(response.data.data.url)
const imgUrl = response.data.data.url
document.querySelector('.my-img').src = imgUrl
})
</script>
总结
对象 | 用途 | 是否能上传文件 |
---|---|---|
FormData |
模拟表单,能包含文本和二进制数据 | ✅ 能上传文件 |
普通对象 {} |
只能传递普通文本数据 | ❌ 不行 |
const fd = new FormData(); // 创建表单数据
fd.append('img', e.target.files[0]); // 把用户选的文件加进去
await axios.post(url, fd); // 发 POST 请求上传
这是文件上传的标准写法,没得简化,必须用 FormData
。
本节素材已上传至Gitee:https://gitee.com/liu-yihao-hhh/ajax_studyhttps://gitee.com/liu-yihao-hhh/ajax_study
四、更换背景
label for 用来关联 id 的值,点到 label 上就跟点击 input 是一个道理
点击更换按钮后,将图片的url地址进行上传至服务器,然后保存到本地,每次页面刷新就先到本地进行查找,如果本地有图片的url就直接更新背景,没有就为空,也不会报错了
/**
* 目标:网站-更换背景
* 1. 选择图片上传,设置body背景
* 2. 上传成功时,"保存"图片url网址
* 3. 网页运行后,"获取"url网址使用
* */
document.querySelector('.bg-ipt').addEventListener('change', async e => {
// console.log(e.target.files[0])
// 上传图片到服务器 要求上传 表单数据
// FormData 是前端用来模拟表单(尤其是含文件)的数据结构
const fd = new FormData()
fd.append('img', e.target.files[0])
const response = await axios.post('http://hmajax.itheima.net/api/uploadimg', fd)
const imgUrl = response.data.data.url
document.body.style.backgroundImage = `url(${response.data.data.url})`
// 2. 上传成功 要保存到本地
localStorage.setItem('bgImg', imgUrl)
})
// 3. 网页运行后,获取url网址
const bgUrl = localStorage.getItem('bgImg')
bgUrl && (document.body.style.backgroundImage = `url(${bgUrl})` )
本节素材已上传至Gitee:https://gitee.com/liu-yihao-hhh/ajax_studyhttps://gitee.com/liu-yihao-hhh/ajax_study
五、 个人信息设置
用户信息获取,这是服务器里面存在的表单数据
但是不是所有DOM元素都是通过.value的形式来进行直接赋值就可以的,比如头像更换和性别的单选框,都是要进行分别单独设置src 和 checked的
// 获取用户信息
const demo = async () => {
const response = await axios.get('http://hmajax.itheima.net/api/settings', {
// params 是专门用在 axios.get() 请求中的,它会自动把你提供的对象转成 URL 的查询字符串
params: {
creator: 'hha'
}
})
const userObj = response.data.data
console.log(userObj)
Object.keys(userObj).forEach(key => {
if (key === 'avatar') document.querySelector('.prew').src = userObj[key]
else if (key === 'gender') {
// 性别
const gRadioList = document.querySelectorAll('.gender')
gRadioList[userObj[key]].checked = true
}
else document.querySelector(`.${key}`).value = userObj[key]
})
}
demo()
头像修改
道理跟上一个案例一模一样,也是提交FormData表单数据到服务器,然后重新添加图片url(avatar属性名) 和 传送人creator 然后提交到服务器put 再通过服务器的数据进行改变本地DOM元素节点的src
document.querySelector('.upload').addEventListener('change', async e => {
console.log(e.target.files[0])
// 提交表单数据
const fd = new FormData()
fd.append('avatar', e.target.files[0])
fd.append('creator', creator = 'hha')
// 提交到服务器
const response = await axios.put('http://hmajax.itheima.net/api/avatar', fd)
console.log(response.data)
document.querySelector('.prew').src = response.data.data.avatar
})
提交表单
利用seialize插件来讲form表单数据json化,然后进行put提交表单数据到服务器
// 提交表单
document.querySelector('.submit').addEventListener('click', async () => {
const userForm = document.querySelector('.user-form')
// serialize 表单插件插件 要在(1. from表单下, 2. DOM标签存在name属性的值)
const userObj = serialize(userForm, { hash: true, empty: true })
// console.log(userObj) // email: 'itheima@itcast.cn', nickname: 'itheima', gender: '1', desc: '我是hha'
// 提交到服务器
userObj.gender = +userObj.gender
console.log(userObj)
const response = await axios.put('http://hmajax.itheima.net/api/settings', userObj)
})