主题
综合案例:个人设置
本页对应 1-6综合案例-个人设置,共 5 段视频,约 54 分钟。
学习目标
- 能够根据接口文档,使用 axios 以查询参数(
params)传递用户外号,发起 GET 请求获取个人设置数据,并从响应对象中正确取出data字段作为用户信息对象 - 能够借助接口文档确认返回数据中每个属性的类型与含义,并运用
Object.keys配合forEach遍历属性名,按标签类别(src/checked/value)分别完成头像、性别、邮箱昵称与个人简介的渲染 - 能够为头像文件选择框绑定
change事件获取用户所选文件,用FormData通过append追加文件与外号两个参数,调用修改头像接口,并将返回的新 URL 同步更新到页面图片上 - 能够为提交按钮绑定点击事件,使用
form-serialize收集表单数据,并按接口文档要求完成性别字段的整数类型转换与creator字段的对象合并,再以 PUT 方式提交 - 能够使用 Bootstrap Toast 组件(获取 DOM 元素、
new bootstrap.Toast()实例化、调用show())在接口成功回调中弹出操作成功提示,并通过data-bs-delay调整自动隐藏的停留时长 - 能够通过浏览器网络面板与控制台验证请求参数、响应数据及刷新后的数据持久化结果,并说明提交数据不符合文档要求时服务器会返回校验失败错误
知识点
案例总览:先看效果,再拆需求
为什么需要:这是个人信息设置案例的介绍课,先要知道“最终要做出什么”,否则直接写代码容易迷路。
怎么想:先运行备课代码看效果,再把效果拆成可实现的步骤。转写中演示了三件事:点击“更换头像”选图后头像立刻变化;修改左侧昵称等信息后点“提交”,刷新页面仍然保留;如果改了但不提交,刷新会还原。
怎么做:本案例整体是“头像修改 + 信息修改”两大块,实际拆成 5 个视频:获取数据、渲染数据、修改头像、修改信息、提示框。需求可归纳为 3 个:信息渲染、修改头像、修改信息。
易错点:不要把“介绍课”当成完整实现课;本节重点是确认效果、步骤和验证方式。验证时常用刷新页面、看网络面板、看控制台输出。
信息渲染之一:获取数据
为什么需要:页面打开后要显示“当前用户”的头像、邮箱、昵称、性别、简介,必须先拿到自己的数据。
怎么想:案例通过“外号”告诉服务器要取谁的数据。外号要定义成变量保存,因为后面修改头像、修改信息还要用同一个外号。
怎么做:按接口文档用 GET 请求,参数放在 params 里。先不写 then,在网络面板预览中确认“获取个人设置成功”,再打印 res,从 res.data 取用户对象存到 userInfo。
易错点:属性含义不要只靠猜单词,要看接口文档的响应结构;转写中反复强调“实际开发看接口文档”。外号变量名和值要在后续接口中保持一致。
js
const creta = '西兰花'
axios({ url: '按接口文档粘贴', params: { creta } }).then(res => {
const userInfo = res.data
console.log(userInfo)
})信息渲染之二:渲染数据
为什么需要:拿到数据后要显示到复杂表单里,包括图片、输入框、单选框、文本域。
怎么想:先找规律。转写中关键标签的类名与数据属性名一致,因此可用 Object.keys(userInfo) 拿到属性名,再 forEach 遍历。设置分三类:头像用 src;性别单选框用 checked;邮箱、昵称、简介用 value。
怎么做:遍历 key,用 if / else if / else 分类处理。性别要先用 querySelectorAll 拿到两个 radio,服务器返回的 gender 是整数:0 男、1 女,正好可作为数组索引。
易错点:querySelectorAll 得到的是类数组,要按索引取;动态属性用中括号;类选择器模板字符串别漏点;性别不是直接写“男/女”,要按文档的 0/1 处理。
js
Object.keys(userInfo).forEach(key => {
if (key === 'avatar') {
document.querySelector(`.${key}`).src = userInfo[key]
} else if (key === 'gender') {
const genderRadios = document.querySelectorAll(`.${key}`)
const gender = userInfo[key]
genderRadios[gender].checked = true
} else {
document.querySelector(`.${key}`).value = userInfo[key]
}
})修改头像:服务器更新后还要更新本地图片
为什么需要:用户选图后不仅要传到服务器,页面也要立刻显示新头像。
怎么想:分三步:获取用户选择的文件、调用修改头像接口、更新本地图片 URL。接口参数比之前多一个外号,用 FormData 追加 avatar 和 creta。
怎么做:给文件输入注册 change 事件,打印 this.files 并取 [0];new FormData() 后 append 两个参数;请求成功后从返回数据中取新头像 URL,设置给 img.src。
易错点:文件索引 [0] 别漏;append 的参数名按文档写;服务器改了不代表页面改了;刷新验证能确认服务器是否真的保存。
js
const data = new FormData()
data.append('avatar', this.files[0])
data.append('creta', creta)
axios({ url: '按接口文档粘贴', method: 'put', data }).then(res => {
const avatar = res.data.avatar
document.querySelector('.avatar').src = avatar
})修改信息:收集、转换、合并、提交
为什么需要:左侧表单要整体提交,且提交数据必须符合接口文档。
怎么想:页面已准备好 form 和 name 属性,可用 form-serialize 快速收集。转写中对比文档发现两处不同:少一个外号 creta;gender 文档要求整数,而表单收集到的是字符串。
怎么做:点击提交后 serialize 得到 formData;用 +formData.gender 转数字;用展开运算符合并 creta 生成新对象;再按文档用 PUT 提交。
易错点:不转类型测试会报“校验失败”;个数、类型都要符合文档;不要忘记外号,否则服务器不知道改谁的数据。
js
const formData = serialize(form, { hash: true, empty: true })
formData.gender = +formData.gender
const data = { ...formData, creta }
axios({ url: '按接口文档粘贴', method: 'put', data }).then(res => {
// 这里接提示框
})提示框:只控制显示,自动隐藏
为什么需要:提交成功后要反馈用户。
怎么想:使用 Bootstrap 的 Toast 组件。页面结构已准备好,JS 只需拿到 DOM、实例化、调用 show();隐藏会自动完成。
怎么做:new bootstrap.Toast(dom).show();显示时长由结构上的 data-bs-delay 控制,单位毫秒,转写中把 1500 改成 3000 测试 3 秒后消失。
易错点:Toast 的 T 大写;不用手动调隐藏;改延迟要改 HTML 自定义属性。
js
const toastDom = document.querySelector('.toast')
const toast = new bootstrap.Toast(toastDom)
toast.show()小结
本案例介绍课要建立整体地图:信息渲染先“获取”再“渲染”;修改头像要“传服务器 + 更新本地”;修改信息要“收集 + 按文档转换合并 + 提交 + 提示”。调试时多用网络面板、控制台和刷新验证;复杂表单不可怕,关键是找规律、看文档、按类型处理数据。
目标与效果
本节只做“个人信息设置”案例的第一步:信息渲染。页面打开后,根据自己准备的外号,从服务器拿到属于自己的用户信息,再把头像、邮箱、昵称、性别、个人简介回显到页面上。
为什么需要先做信息渲染?因为后面的修改头像、修改信息都建立在“页面已经有正确用户数据”的基础上。怎么想?核心只有两步:获取数据 → 渲染数据。怎么做?先准备外号并调接口,确认数据能拿到;再用 Object.keys 遍历属性名,按标签类型分别设置。易错点:不要把“修改头像/修改信息”混进本节,本节只完成回显。
先准备外号
为什么需要外号?转写稿里说得很清楚:想看“自己”的数据,又不想被别人影响,所以给自己起一个外号,并把这个外号告诉服务器。怎么想?外号后面修改头像、修改信息还会复用,最好用变量存起来,不要到处写字符串。怎么做?
js
// 转写中音近 create/creta,实际参数名以接口文档为准
// 这里用 creator 表示“外号”参数
const creator = '西兰花'易错点:外号变量名要和接口参数名对应;后续请求要保持同一个外号,否则服务器不知道改的是谁的数据。
获取数据
为什么需要先获取?页面要回显的内容不在本地写死,而是服务器返回的。怎么想?按接口文档确认请求方法、地址、参数位置。转写中参数在 query,所以用 params。怎么做?
js
axios({
url: '接口文档中的地址',
method: 'get',
params: {
creator
}
}).then(res => {
console.log(res)
const userInfo = res.data.data
})调试过程可以照转写稿走:保存后打开浏览器“网络”面板,点开请求,先看“预览”里是否提示获取成功,再看 data 里是否有“我是西兰花”的数据;然后在控制台打印 res,逐层点开找到用户信息对象。易错点:返回层级容易漏,用户信息通常是 res.data.data,不要直接把 res 当用户对象;params 不要写成 data。
看懂返回结构
为什么需要看结构?因为要确定每个属性对应页面哪个位置。怎么想?转写稿强调:不要只靠猜单词,实际开发更推荐看接口文档的“返回响应/数据结构”。怎么做?核对返回对象中的 5 个属性:avatar、email、name、gender、desc,并确认 gender 是整数:0 表示男,1 表示女。易错点:gender 不是“男/女”字符串,而是数字;后面渲染单选框时要把它当索引使用。
渲染思路:利用类名与属性名一致
为什么可以批量渲染?转写稿里页面关键标签的类名和数据属性名一致,例如头像类名 avatar、邮箱类名 email。怎么想?先用 Object.keys(userInfo) 拿到所有属性名,再 forEach 遍历,每个 key 去页面找对应标签。怎么做?
js
Object.keys(userInfo).forEach(key => {
console.log(key)
})易错点:Object.keys 返回数组;遍历时 key 是字符串;动态取对象属性要用 userInfo[key],不要写 userInfo.key。
头像、性别、文本三类分别设置
为什么不能一套代码设置到底?因为标签类型不同:头像是 img,性别是 radio,邮箱/昵称/简介是输入类元素。怎么想?先分类再逐个击破。怎么做?
js
Object.keys(userInfo).forEach(key => {
if (key === 'avatar') {
document.querySelector(`.${key}`).src = userInfo[key]
} else if (key === 'gender') {
const genderRadios = document.querySelectorAll(`.${key}`)
const gender = userInfo[key]
genderRadios[gender].checked = true
} else {
document.querySelector(`.${key}`).value = userInfo[key]
}
})头像用 src;性别用 querySelectorAll 拿到两个单选框,再用 genderRadios[gender] 找到对应项,设置 checked = true;邮箱、昵称、个人简介统一设置 value。易错点:类选择器模板字符串别漏点;querySelectorAll 返回伪数组;性别不要设置 value,要设置 checked;头像不要设置 value,要设置 src。
调试与小结
怎么验证?保存后刷新页面,看头像是否显示、邮箱昵称简介是否有值、性别是否选中对应项;若不对,回到控制台检查 userInfo 和每个 key。本节小结:信息渲染和图书列表渲染思路一样,都是“获取数据,然后渲染到页面”;区别是这里要根据标签类型选择 src、checked、value。易错点:接口返回层级、动态属性中括号、性别数字索引、类名选择器,是这一段最容易出错的地方。
整体思路:先改服务器,再改页面
这个案例的目标是“修改用户头像并立刻生效”。转写稿里的操作顺序是:点击“更换头像”→选择图片→头像马上变;再刷新页面,头像仍然是新的。这说明服务器已经保存成功,不只是本地预览。
为什么需要:如果只把图片显示到页面,刷新就会丢;如果只传服务器不更新页面,用户会以为没成功。
怎么想:先拿到用户选择的文件,再按接口文档把“头像文件 + 外号”放进 FormData,调用修改头像接口;服务器返回新头像 URL 后,再设置到页面图片上。
怎么做:分四步——获取文件、组织 FormData、调接口、更新页面 URL。
易错点:不要把整个 files 伪数组传过去;外号必须和获取个人信息时一致;接口参数名以文档为准,转写稿中“creta/create”一类读法要注意同音/听写误差。
知识点一:获取用户选择的头像文件
为什么需要:上传的前提是先拿到用户真正选中的那个文件。
怎么想:点击“更换头像”只是标签交互,真正变化的是关联的 input[type=file];文件选择变化应监听 change 事件。
怎么做:给头像输入框绑定 change,在回调里用 this.files[0] 取第一个文件。转写稿里“劝举事件”应纠正为 change 事件,“尾数组”应理解为伪数组。
易错点:回调写成箭头函数时 this 不是输入框;要取 [0],不是把 this.files 整体上传。
js
avatarInput.addEventListener('change', function () {
const file = this.files[0]
console.log(file)
})调试时先看控制台:选择图片后应打印出一个文件对象;若打印的是伪数组,说明还没取索引 0。
知识点二:用 FormData 携带头像和外号
为什么需要:修改头像不是只传文件,还要告诉服务器“改谁的头像”。笔记小结也强调:头像要保存在某个用户名下,所以要把外号一起携带过去。
怎么想:接口文档要求两个参数:头像文件和外号。FormData 正好可以追加多组键值。
怎么做:先 new FormData(),再按文档参数名 append 头像文件和外号。转写稿里“方 data”应纠正为 FormData。
易错点:参数名不能自己猜;外号变量要和获取数据时一致;append 第一个参数是接口字段名,第二个才是值。
js
const formData = new FormData()
formData.append('avatar', this.files[0])
formData.append('外号参数名', creator) // 参数名以接口文档为准网络面板里要能看到 avatar 和对应外号字段都传到了服务器。
知识点三:调用修改头像接口并取回新 URL
为什么需要:服务器保存后,页面还不知道新地址,需要接口返回最新头像 URL。
怎么想:按文档用 PUT,请求体传 FormData;成功后从响应里取头像地址。转写稿里“hen”应纠正为 then,“掉接口”应纠正为“调接口”。
怎么做:用 axios 发请求,data 直接传 formData;在 then 里打印响应,找到 data 下的头像 URL。
易错点:不要漏 method: 'put';不要把 FormData 再包成普通对象;响应层级要看清楚,转写稿中是点开 data 后复制属性路径。
js
axios({
url: '修改头像接口地址', // 以接口文档为准
method: 'put',
data: formData
}).then(res => {
console.log(res)
const avatarUrl = res.data.data.avatar
})调试时看网络面板:预览出现“保存个人头像成功”,说明文件已到服务器。
知识点四:更新页面头像并刷新验证
为什么需要:服务器变了,本地图片不会自动变;必须把新 URL 设置给页面图片。
怎么想:页面头像是 img,改它的 src 即可。转写稿里类名与数据属性名对应,可用类选择器找到图片。
怎么做:拿到 avatarUrl 后,选中头像 img,设置 .src = avatarUrl。
易错点:只调接口不更新页面,用户看不到变化;设置完要刷新验证,刷新后仍是新头像,才证明服务器保存成功。
js
document.querySelector('.avatar').src = avatarUrl最后按转写稿流程验证:更换头像→页面立即更新→刷新页面→头像仍是新的。小结问题的答案是:因为头像要保存到某个用户名下,所以上传时必须把外号一起携带过去。
目标与实现思路
本节要完成的是“个人信息设置”中的“信息修改”:用户在页面上改好昵称、性别、个人简介等信息后,点击提交,把最新数据提交到服务器保存。
为什么需要:前面的信息渲染已经把服务器数据回显到页面,用户现在可以在页面上直接修改。此时页面上的值就是用户最终想保存的值,所以核心不是重新查数据再渲染,而是“收集用户刚改完的数据,提交保存”。
怎么想:它和以前“编辑”功能的思路最接近:先有回显,再收集最新改动,最后提交到服务器。区别是本节表单结构更复杂,但表单标签和 name 属性已经准备好,数据也已经在页面里,因此不需要重新获取并刷新页面。
怎么做:分两步走——先收集表单数据,再调用用户信息更新接口;注意更新接口的请求方法是 PUT,表示更新数据。
易错点:不要把它当成“新增”或“删除”;不要漏掉 PUT;提交的数据必须符合接口文档要求,不能直接把页面收集到的原对象原样发出去。
收集表单数据
为什么需要:页面上有多个表单项,如果一个个用 document.querySelector 取值,代码多且容易漏。表单元素已经设置好 name 属性,适合用表单序列化插件一次性收集。
怎么想:先确认表单结构:左侧区域被一个表单包裹,邮箱、昵称、性别、个人简介等表单元素都有 name。既然结构准备好了,就用 form-serialize 把表单转成对象,方便后续处理。
怎么做:给提交按钮绑定点击事件,在事件里获取表单并序列化。转写稿中的调试过程是先打印 formData,确认能拿到昵称、性别、简介等字段:
js
document.querySelector('.submit').addEventListener('click', () => {
const form = document.querySelector('.user-form')
const formData = formSerialize(form, { hash: true, empty: true })
console.log(formData)
})易错点:hash 和 empty 要按插件用法写对;表单元素没有 name 就收集不到;不要用错选择器,表单类名是 .user-form,提交按钮类名是 .submit。
按接口文档处理数据
为什么需要:页面收集到的数据不一定符合接口文档。转写稿中特别强调:修改信息接口要求的数据有 5 个属性,而表单收集到的是 4 个;同时 gender 要求是整数,页面收集到的却是字符串。
怎么想:先看接口文档,对比“收集到的对象”和“文档要求的数据结构”。个数上少了外号 creator,需要合并进去,告诉服务器改的是谁的数据;类型上 gender 要转成数字,文档中 0 表示男、1 表示女。
怎么做:先把 gender 转成数字,再合并外号:
js
formData.gender = +formData.gender
const data = {
...formData,
creator
}调试时先打印 data,确认对象里有 5 个属性,且 gender 已经是数字。
易错点:转写稿中做过反例:如果不转换 gender 类型,提交后服务器会报“校验失败,请检查必填项字段”等提示。原因是文档要求整数,而字符串不符合要求。另一个易错点是漏掉 creator,服务器无法确认要修改谁的数据。
用 PUT 提交保存
为什么需要:数据准备好后,必须调用更新接口,服务器才会真正保存。更新接口的请求方法是 PUT,不是 GET 或 POST。
怎么想:按接口文档准备 url、method、请求头和请求体。转写稿中接口要求 application/json,所以要把处理后的对象作为 data 传给 axios。
怎么做:
js
axios({
url: '接口文档中的修改个人信息地址',
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
data
}).then(res => {
// 保存成功后的提示,后续用提示框组件完成
})调试时打开网络面板,修改昵称、性别、个人简介后点击提交,确认请求方法为 PUT,请求体里包含 5 个属性;接口返回“保存个人设置成功”后,刷新页面验证数据是否保存。
易错点:method 不能省,必须是 PUT;data 要传处理后的对象,不能传原始 formData;不要忘记设置 JSON 请求头。保存成功后页面本身已经是用户刚写的数据,所以不需要重新获取并刷新页面;刷新只是用来验证服务器是否保存成功。
小结
信息修改和“编辑”实现最接近:回显已经完成,接下来收集用户最新改动,按接口文档整理数据,再用 PUT 提交到服务器保存。重点检查两件事:数据个数是否补齐、数据类型是否符合文档。
目标与使用场景
这一节要完成的是:用户点击“提交”修改个人信息后,如果接口调用成功,就用 Bootstrap 的提示框(Toast)告诉用户“操作成功”。
为什么需要它?因为提交后页面不一定有明显变化,用户会怀疑“到底改没改成功”。提示框就是一种轻量的结果反馈:不打断操作,又能明确告知结果。
怎么想?提示框不能一提交就弹,否则请求失败也会误导用户;应当放在 axios 的成功回调里,等服务器确认成功后再显示。Bootstrap 的 Toast 默认隐藏,显示后会自动消失,我们只需要用 JS 控制“显示”,必要时通过 data-bs-delay 调整停留时间。
准备提示框结构
页面里已经准备好 Bootstrap 提示框结构和提示内容。调试时可以右键检查元素,找到提示框对应的 DOM;它默认不可见,临时去掉隐藏样式能看到内容已经设置好。结构类似:
html
<div class="toast" data-bs-delay="1500">
<div class="toast-body">
操作成功
</div>
</div>data-bs-delay 的单位是毫秒,1500 就是 1.5 秒。转写稿中测试时把它改成 3000,提示框约 3 秒后消失,说明停留时间由这个属性控制。
在成功回调中用 JS 显示
Bootstrap 弹框什么时候用 JS 方式控制显示?答案是:需要先执行一些其他 JS 逻辑,再去显示/隐藏弹框时。这里就是先等 axios 请求成功,再显示提示框。
做法分两步:先拿到提示框 DOM,再用 new bootstrap.Toast(dom元素) 实例化,最后调用 show()。代码放在修改信息接口的 .then 成功回调中:
js
axios({
url: '修改个人信息的接口地址',
method: 'put',
data: data
}).then(res => {
// 请求成功后再提示用户
const toastEl = document.querySelector('.toast')
const toast = new bootstrap.Toast(toastEl)
toast.show()
})注意:show() 只负责显示,不需要手动调用隐藏方法;Toast 会自动隐藏。若希望显示更久,改 HTML 上的 data-bs-delay 即可。
调试过程与易错点
调试时先提交一次修改,打开网络面板确认接口返回“保存个人设置成功”,再看页面是否弹出提示框;随后等待一会儿,确认它自动消失。再把 data-bs-delay 改为 3000,观察是否约 3 秒后消失。
易错点:不要把 toast.show() 写在请求发出之前;不要用 display:block 手动控制,交给 Bootstrap;选择器要写页面中真实存在的类名;new bootstrap.Toast 中 Toast 首字母大写;data-bs-delay 单位是毫秒。最后记住本节小结:需要执行其他 JS 逻辑后再显示/隐藏弹框时,就用 JS 方式控制 Bootstrap 弹框。
课件图示




代码示例
代码 1:信息渲染(获取数据 + 回显)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>个人信息设置 - 信息渲染</title>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>
<body>
<form class="user-form">
<img class="avatar" src="" alt="头像">
<label>邮箱</label>
<input class="email" name="email" type="text">
<label>昵称</label>
<input class="name" name="name" type="text">
<label>性别</label>
<input class="gender" name="gender" type="radio" value="0"> 男
<input class="gender" name="gender" type="radio" value="1"> 女
<label>个人简介</label>
<textarea class="desc" name="desc"></textarea>
<button type="button" class="submit">提交</button>
</form>
<script>
// ⚠️ 接口地址请从自己的接口文档里复制,这里只是占位
const API = {
profile: 'http://localhost:8080/api/user/profile' // 获取 / 修改个人信息
}
// 1. 外号存成变量,后面改头像、改信息都要用同一个
const creator = '西兰花'
// 2. 获取数据(query 参数用 params 传)
axios({
url: API.profile,
params: { creator } // → ?creator=西兰花
}).then(res => {
const userInfo = res.data
// 3. 渲染数据:数据属性名 ←→ 页面类名 一一对应
Object.keys(userInfo).forEach(key => {
if (key === 'avatar') {
// 头像:img 的 src
document.querySelector(`.${key}`).src = userInfo[key]
} else if (key === 'gender') {
// 性别:单选框的 checked,用返回的 0/1 当下标
const genderRadios = document.querySelectorAll(`.${key}`)
genderRadios[userInfo[key]].checked = true
} else {
// 邮箱 / 昵称 / 个人简介:value
document.querySelector(`.${key}`).value = userInfo[key]
}
})
})
</script>
</body>
</html>要点
params才是 query 参数,写在data里会变成请求体。- 真正要用的用户数据是
res.data,不要直接拿res。 - 三类标签三种写法:
img用src、radio用checked、input/textarea用value。 - 性别是整数 0/1,恰好等于
querySelectorAll结果的索引,所以genderRadios[gender].checked = true一步到位。 - 易错点:性别有两个标签,必须用
querySelectorAll;querySelector只会拿到第一个男的。模板字符串的反引号和${}别漏。
代码 2:头像修改(立刻生效)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>个人信息设置 - 修改头像</title>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>
<body>
<img class="avatar" src="" alt="头像">
<label for="upload">更换头像</label>
<input type="file" id="upload" hidden>
<script>
// ⚠️ 接口地址请从自己的接口文档里复制,这里只是占位
const API = {
avatar: 'http://localhost:8080/api/user/avatar' // 修改个人头像
}
const creator = '西兰花'
document.querySelector('#upload').addEventListener('change', function () {
// 1. 拿到用户选择的头像文件
const file = this.files[0]
// 2. 组装 FormData:接口要几个参数就 append 几个
const fd = new FormData()
fd.append('avatar', file) // 新头像文件
fd.append('creator', creator) // 外号:告诉服务器改谁的头像
// 3. 提交到服务器
axios({
url: API.avatar,
method: 'PUT',
data: fd
}).then(res => {
// 4. 服务器保存成功,还要把返回的新 URL 设置回页面
document.querySelector('.avatar').src = res.data.avatar
})
})
</script>
</body>
</html>要点
- 事件是
change(选完文件才触发),回调用function才能用this.files。 this.files是伪数组,取[0]才是那个文件。FormData里append的参数名必须照接口文档抄,且别漏creator,否则服务器不知道改谁的头像。- 请求方法是
PUT,data直接传fd,axios 会自动带正确的Content-Type。 - 最容易漏的一步:只调接口不改页面,用户会以为没成功。必须把
res.data.avatar赋给img.src;刷新后仍是新头像才算真正保存成功。
代码 3:信息修改 + Bootstrap 提示框
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>个人信息设置 - 修改信息</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css">
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/form-serialize/index.min.js"></script>
</head>
<body>
<form class="user-form">
<input class="email" name="email" type="text">
<input class="name" name="name" type="text">
<input class="gender" name="gender" type="radio" value="0"> 男
<input class="gender" name="gender" type="radio" value="1"> 女
<textarea class="desc" name="desc"></textarea>
<button type="button" class="submit">提交</button>
</form>
<!-- 提示框结构:默认不可见,用 JS 控制显示 -->
<div class="toast my-toast" data-bs-delay="3000">
<div class="toast-body">操作成功</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script>
// ⚠️ 接口地址请从自己的接口文档里复制,这里只是占位
const API = {
profile: 'http://localhost:8080/api/user/profile' // 获取 / 修改个人信息
}
const creator = '西兰花'
document.querySelector('.submit').addEventListener('click', () => {
// 1. 收集表单数据(name 属性必须齐全)
const form = document.querySelector('.user-form')
const formData = serialize(form, { hash: true, empty: true })
// 2. 按接口文档处理数据
formData.gender = +formData.gender // 字符串 "1" → 整数 1
const data = { ...formData, creator } // 合并外号,4 个变 5 个
// 3. 提交保存(PUT 表示更新)
axios({
url: API.profile,
method: 'PUT',
data
}).then(res => {
// 4. 用 JS 方式弹出提示框
const toastDom = document.querySelector('.my-toast')
const toast = new bootstrap.Toast(toastDom)
toast.show()
})
})
</script>
</body>
</html>要点
serialize(form, { hash: true, empty: true })直接返回对象,前提是表单里每个元素都写了name。- 收集到的
gender是字符串(如"1"),文档要 integer,用+转成数字;不转服务器会返回校验失败报错。 - 接口要 5 个参数,表单只有 4 个,必须
{ ...formData, creator }合并,且外号要和获取数据时完全一致。 - 更新用
PUT;成功后页面已经是用户刚填的数据,不需要重新请求刷新。 - 提示框:
new bootstrap.Toast(dom)创建实例 →.show()显示,不用手动隐藏,到data-bs-delay毫秒后自动消失。 - 易错点:提交按钮要写
type="button",否则会触发表单默认提交导致页面刷新、请求被打断。
常见错误
现象:页面回显的不是自己的数据,或修改后改到别人头上。
原因: 获取信息、上传头像、提交信息时使用的create外号不一致或中途写死。
修法: 全案例统一使用同一个create变量,获取、FormData.append、请求体合并都引用它。现象:头像上传接口成功,但刷新后仍是旧头像。
原因: 只更新了服务器,未用接口返回的头像 URL 更新页面img.src。
修法: 在成功回调中取返回头像地址,立即设置到页面头像元素上,再刷新验证服务器是否持久化。现象:信息渲染后头像不显示、性别不选中,或输入框全空。
原因: 遍历Object.keys(userInfo)后统一按value设置,未区分控件类型。
修法: 按属性名分类处理:头像设src,性别设对应 radio 的checked,邮箱/昵称/简介设value。现象:性别男/女勾选反了,或两个都不选中。
原因: 未按接口文档把gender的0/1与 radio 数组索引对应,或选错 radio 元素。
修法: 用gender作为 radio 数组索引取元素,再设置checked = true;对照文档确认0男、1女。现象:修改信息接口报“校验失败/请检查必填项字段”。
原因: 直接把serialize结果提交,未把gender转成整数,也未合并create。
修法: 提交前执行+formData.gender转数字,并用展开运算符合并{...formData, create},字段数量和类型对照接口文档。现象:修改信息接口不生效或请求失败。
原因: 请求方法写成POST/PATCH,或请求体放错位置。
修法: 按接口文档使用method: 'put',请求体放到data中,并确认字段名与文档一致。现象:提交成功后提示框不出现、不消失或停留时间不对。
原因: 只创建 Toast 未调用show();或误以为需要手动隐藏;或data-bs-delay单位/值写错。
修法:new bootstrap.Toast(el).show();隐藏交给组件自动处理;通过data-bs-delay按毫秒调整停留时间。现象:选择头像后
this.files为空或拿不到文件。
原因:change事件未绑定到真正触发文件选择的input[type=file]。
修法: 确认事件源是 file input,取this.files[0];若通过 label 关联,需定位到对应 input 再绑定/取值。
练习
给定
userInfo = { avatar, email, nickname, gender, desc },写出渲染逻辑,要求按“头像、性别、其余输入项”三类分别设置正确的 DOM 属性。写出头像修改从“选择文件”到“页面头像更新”的关键代码结构,并标明
FormData中必须携带的两个字段及用途。现有
formData = { email, nickname, gender: '1', desc }和create变量,按接口文档要求构造 PUT 请求体;说明提交前需要做的两处数据加工。在修改信息成功的回调中展示 Bootstrap Toast,并让提示框停留 3 秒;写出关键 JS 与 HTML 属性设置。
排错题:用户反馈“改了昵称并提交,刷新后仍是旧昵称”。请列出至少 3 个排查点,并说明如何快速定位。
原视频对照
| # | 视频 | 时长 |
|---|---|---|
| 1 | 案例-个人设置-获取数据 | 08:58 |
| 2 | 案例-个人设置-渲染数据 | 13:30 |
| 3 | 案例-个人设置-修改头像 | 10:50 |
| 4 | 案例-个人设置-修改信息 | 13:37 |
| 5 | 案例-个人设置-提示框 | 07:12 |
小结
- 本案例三条主线是:信息回显、头像修改、信息提交,三者都依赖同一个
create外号贯穿请求。 - 复杂表单渲染先找规律再分类处理,不要按标签数量逐个硬写。
- 头像修改必须同时完成“服务器保存”和“页面 URL 更新”,否则用户会误判失败。
- 信息提交前要以接口文档为准做数据加工,重点检查字段数量、字段类型和请求方法。
- 提交成功后再触发用户提示;Toast 只需控制显示,隐藏与停留时间由组件和
data-bs-delay管理。 - 验证修改是否真正成功,优先刷新页面看回显,而不是只看一次接口成功提示。
- 排查此类案例优先查:外号是否一致、参数名/类型是否符合文档、请求方法是否正确、页面是否同步更新。