主题
接口与用户登录案例
本页对应 1-3接口+用户登录案例,共 5 段视频,约 54 分钟。
学习目标
- 能够根据接口文档准确识别 URL、请求方法、参数位置及参数约束,并在 Axios 请求中正确设置
url、method、data等配置项。 - 能够实现用户登录的点击提交逻辑,获取用户名和密码并提交到服务器,同时根据接口文档完成用户名和密码长度校验及失败阻断。
- 能够封装可复用的提示框函数,通过参数控制提示文本和成功/失败外观,并使用定时器实现显示后自动隐藏及类名清理。
- 能够使用
form-serialize插件快速收集表单数据,正确配置hash和empty,并说明表单元素name属性与接口参数名的对应关系。 - 能够将
form-serialize整合到用户登录案例中,通过解构获取用户名和密码变量,并验证原有校验、登录成功与失败提示功能均正常。
知识点
接口文档:前后端协作的“接口说明书”
为什么需要:实际开发中,前端负责页面布局、交互和用 AJAX/axios 与服务器通信,后端负责提供数据接口。前端不能靠猜 URL、请求方法和参数,否则请求一定对不上。接口文档就是后端提供的“接口说明书”。
怎么想:不要把它想复杂,文档就是描述接口的文章。看文档时先抓三要素:请求 URL、请求方法、请求参数。参数还要看位置:query 表示查询参数,axios 里放 params;body 表示请求体参数,axios 里放 data。转写稿中城市列表接口的 pname 在文档里标为 query,所以之前代码放在 params 里;登录接口是 POST,参数在 body,所以放 data。
怎么做:打开接口文档,找到登录接口,记录 URL、POST 方法、body 参数中的 username 和 password,并留意类型和说明,例如用户名最少 8 位、密码最少 6 位。文档里出现 application/json,表示提交数据应为 JSON 格式;axios 的 data 传普通对象时会自动转成 JSON。
易错点:不同公司文档样式可能不同,但核心仍是 URL、方法、参数;不要把 body 参数误放到 params;方法大小写在演示中不敏感,但建议按文档写;参数名必须与文档一致。
登录案例:先完成通信,再替换输入值
为什么需要:登录是常见业务,必须把用户输入提交给服务器并处理成功/失败。
怎么想:先写 axios 核心请求,把文档信息填进去;再把写死的用户名、密码替换成页面输入框的 value;最后用 then 和 catch 看服务器返回。
怎么做:绑定登录按钮点击事件,获取用户名和密码,发起请求:
js
axios({
url: '接口文档中的URL',
method: 'post',
data: { username, password }
}).then(res => {
console.log(res.data.message)
}).catch(err => {
console.log(err.response.data.message)
})调试时打开开发者工具 Network,看响应报文里的 message;成功时提示“登录成功”,密码错误时提示“用户名或密码错误”。转写稿中还演示了右键“复制属性路径”,快速得到 res.data.message 和 err.response.data.message。
易错点:先获取输入值再发请求;失败信息在 err.response.data.message,不是 err.data;不要只靠 console.log 作为最终交互。
长度校验:不符合要求就不要发请求
为什么需要:空用户名、空密码或长度不足的数据没必要提交服务器,先在前端拦住能减少无效请求。
怎么想:长度要求来自接口文档,不是随便写。用户名最少 8 位,密码最少 6 位。判断放在请求之前,不符合就提示并 return。
怎么做:
js
if (username.length < 8) {
myAlert('用户名长度应大于等于8', false)
return
}
if (password.length < 6) {
myAlert('密码长度应大于等于6', false)
return
}易错点:忘记 return 会导致提示后仍继续发请求;长度判断要在 axios 之前;提示文案可按实际需求调整。
用函数封装提示框
为什么需要:用户名失败、密码失败、登录成功、登录失败四处都要提示,若复制四遍代码会冗余且难维护。
怎么想:提示框要控制四件事:显示、延时隐藏、内容、外观。内容不确定就作为参数;成功/失败只有两种,用布尔值控制类名。
怎么做:封装 myAlert(message, flag),显示时加 show 类,设置 innerText,根据 flag 加成功或失败类名;延时后移除 show,同时移除成功/失败类名。转写稿中曾出现“成功后又失败”的 bug:成功和失败类名同时存在,Bootstrap 中 alert-danger 优先级更高,所以显示失败样式。解决方法是隐藏时把动态类名也 remove 掉。
易错点:只 add 不 remove 会残留类名;只放文本时用 innerText;类名要从 Bootstrap 文档确认,如 alert-success、alert-danger。
用 form-serialize 快速收集表单值
为什么需要:表单项一多,逐个 querySelector 再取 value 很麻烦。插件能一次性把表单内带 name 的元素收集成对象。
怎么想:核心是 serialize(form, { hash: true, empty: true })。hash: true 返回对象,false 返回查询字符串;empty: true 收集空数据,保证格式与表单一致,推荐使用。
怎么做:先导包,给 form 加类名,给输入框设置 name,且 name 必须按接口文档来,如 username、password。然后:
js
const form = document.querySelector('.login-form')
const data = serialize(form, { hash: true, empty: true })
const { username, password } = data整合后替换原来逐个取值的代码,后续长度判断、axios 请求、提示框逻辑都不变。
易错点:表单元素必须放在 form 内;name 不能乱写,要与接口文档参数名一致;忘记导包会报错;empty: false 时空字段可能不收集,取值容易拿不到属性。
先明确本节要完成的登录主线
为什么需要:用户登录是绝大多数网站的基础业务。它不只是“点一下按钮”,而是要把用户输入的用户名、密码按接口要求提交给服务器,并根据结果做后续反馈。
怎么想:先看运行效果,再拆步骤。完整登录一般包括:点击登录后获取并判断用户名、密码长度;符合要求再提交给服务器;最后把成功或失败信息提示给用户。本节先完成前两步,提示框留到后续完善。
怎么做:先运行备课代码观察效果,再打开接口文档,找到登录接口的 URL、请求方法和参数要求,然后回到页面写代码。
易错点:不要一上来就写提示框;先把“取数据—判断—提交”跑通。转写稿里的“这口文档”应理解为“接口文档”,“excus/excess”应纠正为 axios。
绑定登录按钮的点击事件
为什么需要:登录动作由用户点击触发,必须先给按钮绑定点击事件,后续取值和请求才有入口。
怎么想:用 document.querySelector 找到登录按钮,再用 addEventListener 监听 click。转写稿提到按钮类名很多,前两个是 Bootstrap 类名,实际应选业务类名;具体选择器以页面审查元素为准。
怎么做:
js
const loginBtn = document.querySelector('登录按钮选择器')
loginBtn.addEventListener('click', () => {
// 后续登录逻辑
})易错点:选择器不要写错;类名有多个时不要随便拿一个;回调建议用箭头函数,避免 this 干扰。转写稿里的“quary select”应纠正为 querySelector。
从输入框获取用户名和密码
为什么需要:提交给服务器的不能是写死的数据,而应是用户在页面输入框里实际输入的内容。
怎么想:先找到用户名输入框和密码输入框,再读取它们的 value 属性。转写稿里的“where”应纠正为 value。
怎么做:
js
const usernameInput = document.querySelector('用户名输入框选择器')
const passwordInput = document.querySelector('密码输入框选择器')
const username = usernameInput.value
const password = passwordInput.value
console.log(username, password)调试时可以先在控制台输出,确认输入 IT黑马001、123456 后能拿到对应值,再写后续逻辑。
易错点:不要忘记 .value;输入框选择器要与页面类名一致;变量名要能区分输入框元素和值。
按接口文档判断长度,并用 return 阻断
为什么需要:用户名或密码长度不符合要求时,数据没必要提交到服务器,否则会产生无效请求。
怎么想:长度要求不能猜,要看接口文档。转写稿中登录接口要求用户名最少 8 位、密码最少 6 位。判断不通过时,先给简单提示占位,并立即 return 阻断后续代码。
怎么做:
js
if (username.length < 8) {
alert('用户名长度应大于等于8')
return
}
if (password.length < 6) {
alert('密码长度应大于等于6')
return
}易错点:只提示不 return,后面的 axios 仍会执行;length 不要写成“let”;密码要求是 6 位,不要和用户名的 8 位混淆。转写稿里的“R而”应纠正为 return。
用 axios 提交数据并验证响应
为什么需要:登录最终要和服务器通信,必须按接口文档提交用户名和密码。
怎么想:登录接口是 POST;参数属于 body 参数,要放在 axios 的 data 里,而不是 params。参数名按接口文档写为 username、password。axios 会把普通对象转成 JSON,不需要自己手动 JSON.stringify。
怎么做:
js
axios({
url: '登录接口地址',
method: 'post',
data: {
username,
password
}
}).then(res => {
console.log(res.data.message)
}).catch(err => {
console.log(err.response.data.message)
})调试时打开浏览器 Network 面板:先看请求是否发出,再看请求体是否为 JSON,最后看响应报文。成功时响应里会有“登录成功”;密码改错后,响应里会出现“用户名或密码错误”。转写稿里的“messenage”应纠正为 message。
易错点:URL 要从接口文档复制;data 不要误写成 params;成功和失败分支都要看响应结构;属性路径可用“复制属性路径”辅助确认,如 res.data.message。
小结
本节主线是:绑定登录按钮点击事件 → 从输入框获取用户名和密码 → 按接口文档判断长度并用 return 阻断无效提交 → 基于 axios 提交数据 → 在 Network 和响应报文中验证成功、失败结果。提示框的封装和 form-serialize 的整合属于后续完善,先把这条主线跑通,再逐步替换临时提示和取值方式。
需求:为什么要把提示信息封装成函数
用户登录案例里,提示信息会出现在四个位置:用户名长度不符合要求、密码长度不符合要求、登录成功、登录失败。四个位置都要“显示提示框、切换文字、切换成功/失败外观、过一会儿自动消失”。如果每处都复制一遍显示和隐藏代码,逻辑重复,后续改样式或改消失时间就要改四次,容易漏改。
所以这里的关键判断是:遇到相同逻辑、重复代码要复用时,就封装函数。封装前先明确需求:函数需要接收“提示文字”和“成功/失败标记”;函数内部负责拿到提示框 DOM、显示、改文字、加外观类、定时隐藏。
怎么想:参数与类名控制
提示框本身由准备好的 HTML 和样式提供。转写稿中分析到,弹框显示与隐藏的本质是类名 show:有 show 时透明度为 1,没有时透明度为 0。因此函数中不需要直接改 style,只要 classList.add('show') 和 classList.remove('show')。
文字用参数 msg 传入,因为不同场景提示内容不同;外观只有成功和失败两种,用布尔值 flag 控制:true 用成功类,false 用失败类。成功、失败类名来自 Bootstrap 文档中的 alert-success 和 alert-danger,使用库或插件时查文档、善用搜索即可。
怎么做:核心代码与调用位置
在登录逻辑中封装如下函数,保留本节核心代码:
js
// 4. 封装提示框函数
function myAlert(msg, flag) {
// 获取弹框的dom元素
const alertDom = document.querySelector('.my-alert')
// 显示弹框 添加 show类名
alertDom.classList.add('show')
// 更改弹框的内容-使用msg的内容
alertDom.innerText = msg
// 根据flag 控制外观 成功true- alert-success / 失败false- alert-danger
alertDom.classList.add(flag ? 'alert-success' : 'alert-danger')
// 过一会,隐藏弹框
setTimeout(() => {
alertDom.classList.remove('show')
alertDom.classList.remove(flag ? 'alert-success' : 'alert-danger')
}, 1000);
}调用时按四个场景传不同文字和布尔值:用户名判断失败、密码判断失败传失败提示;登录成功传成功提示;登录失败传失败提示。这样哪里需要提示,直接调用 myAlert。
易错点:类名残留与调试过程
转写稿中演示了一个典型 bug:先显示成功提示,再显示失败提示,最后成功提示出现时却仍显示失败样式。原因是显示时加了成功/失败类名,隐藏时只移除了 show,没有移除外观类;两个外观类同时存在时,失败类优先级更高,覆盖成功类。解决方法是:定时隐藏时,除了移除 show,还要移除本次根据 flag 加上的外观类,即代码中的第二个 classList.remove。
另外注意:DOM 获取一次即可,不必在显示和隐藏中重复 querySelector;提示文字用 innerText 设置文本;setTimeout 的时间按代码为 1000 毫秒,笔记讲解提到“2秒”与核心代码不一致,实际以代码为准,也可按需求调整。默认页面中若提示框带 show,应先移除,保证初始隐藏。
为什么需要 form-serialize 插件
为什么需要: 前面收集表单值时,是一个个标签获取:先 querySelector 找到输入框,再点 value。如果表单里有很多表单元素,就要重复写很多次,麻烦且容易漏。
怎么想: 既然目标是把一个 form 范围内的表单元素值一次性收集出来,就应该找“按表单收集”的工具,而不是继续逐个取值。form-serialize 插件提供的 serialize 函数就是做这件事的。
怎么做: 先把插件引入网页,再准备 form 表单和表单元素的 name 属性,最后调用 serialize(form, 配置对象)。
易错点: 要取值的表单元素必须放在这个 form 表单里面;表单元素必须设置 name,它会成为收集结果中的属性名;name 的值不能乱写,要和接口文档里的参数名一致。
serialize 函数的两个参数
为什么需要: 只传 form 还不够,因为收集结果可能是对象,也可能是查询字符串;空值是否收集也需要控制。
怎么想: 第一个参数确定“收集谁”,第二个参数确定“收集成什么样”。
怎么做: 参数1是 form 表单标签对象;参数2是配置对象。hash: true 收集为 JS 对象,hash: false 收集为查询字符串;empty: true 收集空值,empty: false 不收集空值。
易错点: 日常开发更常用 hash: true,因为拿到对象后可以直接按属性名取值;empty 推荐用 true,这样空数据也会保留,收集结果的格式和表单结构一致,后续取值时属性一定存在。若用 false,没有值的表单项不会出现在结果里,格式就不一定和表单一致。
完整示例代码
为什么需要: 只看语法不够,要放到页面里验证点击提交后能否一次性拿到用户名和密码。
怎么想: 给提交按钮绑定点击事件,在事件里找到 form,再调用 serialize,最后输出结果观察格式。
怎么做: 保留下面笔记中的完整代码,重点看导包、form 结构、name 属性和 serialize 调用。
html
<!DOCTYPE html>
<html lang="zh-CN">
<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>form-serialize</title>
</head>
<body>
<form action="javascript:;" class="example-form">
<input type="text" name="uname" />
<br>
<input type="text" name="pword" />
<br>
<input type="submit" class="submit" value="提交" />
</form>
<!-- 1. 导包 -->
<script src="./lib/form-serialize.js"></script>
<script>
/*
form-serialize 快速收集表单的值
1. 表单元素需要设置name属性,会成为对象的属性名
2. name属性设置的值和接口文档一致即可
3. hash 设置生成的数据格式
- true:js对象(常用)
- false:查询字符串
4. empty 是否收集空数据
- true:收集空数据,保证格式和表单一致(推荐用法)
- false:不收集空数据,格式和表单不一定一致(不推荐)
*/
document.querySelector('.submit').addEventListener('click', () => {
// console.log('点了按钮')
const form = document.querySelector('.example-form')
// 参数1 form表单,要取值的表单元素需要在form表单中
// 参数2 插件的设置,{}
const data = serialize(form, { hash: true, empty: true })
// const data = serialize(form, { hash: false, empty: true })
// const data = serialize(form, { hash: true, empty: false })
console.log(data)
})
</script>
</body>
</html>易错点: 不要忘记先导包;按钮点击事件里要先拿到 form,而不是拿到某个 input;如果 name 写成 uname、pword,那么对象属性就是 uname、pword,不会自动变成 username、password。
整合到用户登录案例
为什么需要: 登录案例里原本要分别获取用户名输入框和密码输入框的 value,再提交给服务器。用插件后可以一次性收集,减少重复代码。
怎么想: 先确认 form 表单有没有方便选择的类名,再确认用户名、密码输入框的 name 是否与接口文档一致,然后用 serialize 的返回值替代原来的逐个取值。
怎么做: 给 form 加类名,例如 login-form;给输入框设置 name="username"、name="password";调用:
js
const form = document.querySelector('.login-form')
const data = serialize(form, { hash: true, empty: true })
const { username, password } = data这样后续代码里仍然可以使用 username 和 password 两个变量,原逻辑改动较小。
易错点: name 的值要按接口文档来写,文档里是 username 就写 username,是 password 就写 password;解构时属性名必须和 name 一致;不要忘记导包路径要写对,例如 ./lib/form-serialize.js。
调试与小结
调试过程: 在示例页输入用户名和密码,点击提交,控制台会输出一个对象,属性名来自表单元素的 name,属性值来自输入框的 value。把 hash 改为 false,输出会变成类似查询字符串的格式;把 empty 改为 false,空输入不会被收集。整合登录案例后,可以先输出 data 确认对象结构,再解构出 username、password,继续执行原来的长度判断和登录请求逻辑。
小结: form-serialize 适合快速收集表单元素的值。核心步骤是:引入插件 → 准备 form 和 name 属性 → 调用 serialize(form, { hash: true, empty: true })。记住:hash 决定对象还是查询字符串,empty 决定是否收集空值,name 要和接口文档一致。
为什么要用 form-serialize 重构登录案例
为什么需要:上一版登录案例收集用户名和密码时,是分别用 document.querySelector 找到输入框,再读取 .value。表单元素一多,就要重复写很多次,代码冗长且容易漏取。
怎么想:form-serialize 的核心价值是“一次收集整个 form 表单里带 name 的表单元素值”,并整理成一个对象。登录案例刚好有用户名、密码两个输入框,适合把原来的“逐个取值”替换成“整表收集”。
怎么做:先不要急着改 JS,先确认页面里确实有一个 form 表单,并且用户名、密码输入框都在这个表单内部。然后给表单加一个方便查找的类名,例如 login-form;给两个输入框补上 name 属性。name 的值不能随便写,要按接口文档里的参数名来,通常就是 username 和 password。
易错点:表单元素没有放在 form 内部,插件收集不到;输入框没有 name,生成对象里就没有对应属性;name 写错,后续解构拿不到值。
引入插件并替换原来的取值代码
为什么需要:插件必须先引入才能调用。原来登录逻辑里已经有“获取用户名、密码”的代码,我们只需要替换这一段,不要重写整个案例。
怎么想:转写稿里反复强调“修改一点就确认测试一下”。所以先引入插件,再找到原来写 const username = ...value、const password = ...value 的位置,用插件调用替换掉。
怎么做:按项目实际路径引入插件后,核心调用是:
js
import serialize from './form-serialize.js'
const form = document.querySelector('.login-form')
const data = serialize(form, {
hash: true,
empty: true
})
console.log(data)这里的 hash: true 表示返回对象;empty: true 表示空数据也收集,保证对象结构和表单一致。先输出 data,确认能得到类似:
js
{ username: 'IT黑马001', password: '123456' }易错点:hash 不是“哈西”,empty 不是“M/T”;true 不要写成 Q 或“臭”;serialize 不要拼成 size、Cize。参数二必须是对象,不要只传一个布尔值。
用解构让后续代码少改
为什么需要:插件返回的是对象,而原登录逻辑后面一直使用 username、password 两个变量。如果每个地方都写 data.username,改动面会变大。
怎么想:对象里已经有 username、password 属性,可以用解构直接赋给同名变量,这样后面的长度判断、请求发送、提示逻辑几乎不用动。
怎么做:
js
const { username, password } = data
console.log(username, password)确认能打印出用户输入的用户名和密码后,再放开原来的判断和请求代码。转写稿中的调试顺序是:先输出整个对象,再解构输出两个变量,最后验证空用户名、空密码、正确密码、错误密码四种情况。
易错点:解构属性名必须和输入框 name 完全一致;解构时逗号不能漏;如果接口文档要求的是 username,就不要写成 user name 或 userName。
调试与验证过程
为什么需要:整合插件后,表面上看只是换了一种取值方式,但表单结构、name、解构、后续请求都可能互相影响,必须逐项验证。
怎么想:先验证“取值”,再验证“校验”,最后验证“请求”。不要一上来就点登录看结果,否则出错时不知道是插件没取到值,还是后续逻辑有问题。
怎么做:在控制台先看 data 是否为对象;再看解构后的 username、password 是否有值;然后测试用户名为空时是否提示并阻止请求;密码为空或长度不足时是否提示;最后输入正确账号密码,在浏览器 Network 面板查看请求参数和响应信息,确认登录成功;再故意改错密码,确认失败提示正常。
易错点:只打印对象不打印解构变量,可能误以为后续变量有值;表单没加类名导致 querySelector 选不到;输入框 name 与接口文档不一致,请求参数名就错了。
本节小结
把第三方插件用到已完成案例中,正确姿势是:先引入,再只替换目标代码,改一点测一点。form-serialize 的关键步骤是:确认 form 结构 → 给表单元素设置符合接口文档的 name → 调用 serialize(form, { hash: true, empty: true }) → 用解构取出 username、password → 回归验证原有登录逻辑。这样既减少了重复取值代码,也保留了原案例的校验、请求和提示功能。
课件图示






代码示例
代码一:form-serialize 快速收集表单值(可独立运行)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>form-serialize 快速收集表单值</title>
</head>
<body>
<form action="javascript:;" class="example-form">
<input type="text" name="username"><br>
<input type="text" name="password"><br>
<input type="submit" class="submit" value="提交">
</form>
<!-- 1. 导包:先引入插件,再用 serialize -->
<script src="./lib/form-serialize.js"></script>
<script>
document.querySelector('.submit').addEventListener('click', () => {
// 参数1:form 表单对象(要取值的表单元素必须在这个 form 内部)
const form = document.querySelector('.example-form')
// 参数2:配置对象
// hash: true → 收集成 JS 对象(常用);false → 查询字符串
// empty: true → 空值也收集,保证对象格式和表单一致(推荐)
const data = serialize(form, { hash: true, empty: true })
console.log(data) // { username: '', password: '' }
})
</script>
</body>
</html>要点解释
- 关键行
serialize(form, { hash: true, empty: true }):第一个参数必须是 form 表单元素,不是某个 input;插件只会收集 form 内部的表单元素。 - 表单元素必须写
name属性,name的值会成为结果对象的属性名。name 值必须和接口文档里的参数名一致(登录接口是username、password),不能自己随便起。 hash: false时得到的是username=&password=这种查询字符串格式;日常开发一般用hash: true。empty: false时空值不收集,结果对象的属性可能缺失,后续取值要判空,所以推荐empty: true。- 易错点:
<script src="./lib/form-serialize.js">必须放在业务脚本之前,否则serialize未定义。 - 易错点:form 上用
action="javascript:;"(或用type="button"),否则点提交会刷新页面,控制台内容一闪而过。
代码二:封装提示框函数 myAlert(可独立运行)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>封装提示框函数</title>
<style>
.my-alert {
position: fixed;
top: 20px;
left: 50%;
transform: translateX(-50%);
padding: 10px 24px;
border-radius: 4px;
color: #fff;
opacity: 0; /* 默认透明 = 隐藏 */
transition: opacity .3s;
pointer-events: none; /* 隐藏时不挡住页面点击 */
}
.my-alert.show { opacity: 1; } /* 加上 show 类名 = 显示 */
.alert-success { background: #28a745; }
.alert-danger { background: #dc3545; }
</style>
</head>
<body>
<!-- 提示框标签,默认没有 show 类名,所以看不见 -->
<div class="my-alert"></div>
<button class="btn-ok">模拟登录成功</button>
<button class="btn-fail">模拟登录失败</button>
<script>
// 4. 封装提示框函数:msg = 提示内容,isSuccess = 外观(true 绿 / false 红)
function myAlert(msg, isSuccess) {
// 获取弹框的 dom 元素
const alertDom = document.querySelector('.my-alert')
// 显示弹框:添加 show 类名
alertDom.classList.add('show')
// 更改弹框的内容
alertDom.innerText = msg
// 根据 isSuccess 控制外观:成功 alert-success / 失败 alert-danger
alertDom.classList.add(isSuccess ? 'alert-success' : 'alert-danger')
// 过一会隐藏弹框,并把外观类名一起移除
setTimeout(() => {
alertDom.classList.remove('show')
alertDom.classList.remove(isSuccess ? 'alert-success' : 'alert-danger')
}, 1000)
}
document.querySelector('.btn-ok').addEventListener('click', () => myAlert('登录成功', true))
document.querySelector('.btn-fail').addEventListener('click', () => myAlert('用户名或密码错误', false))
</script>
</body>
</html>要点解释
- 显示 / 隐藏的本质是加类名 / 删类名:
.my-alert默认opacity: 0,加上show后opacity: 1。不要用display写死,也不要用style.opacity手动改。 - 内容用
innerText,因为只放纯文本、不需要渲染 HTML 结构。 - 定时器里
remove的类名必须和add的是同一个(都用isSuccess ? 'alert-success' : 'alert-danger'判断)。 - 易错点(笔记里踩过的坑):只在显示时
add、隐藏时只删show,会残留alert-success或alert-danger。两个类名同时存在时,alert-danger优先级更高,就会出现「登录成功了却一直是红色」的 bug。所以隐藏时要把外观类名一起remove。 - 验证方法:快速连点「成功」「失败」两个按钮,观察弹框消失后元素上是否还残留
alert-success/alert-danger。 - 为什么封装:用户名不合格、密码不合格、登录成功、登录失败共 4 处都要弹提示,重复代码必须抽成函数。
代码三:用户登录完整案例(axios + form-serialize + myAlert)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>案例:用户登录</title>
<style>
body { font-family: sans-serif; padding: 60px; }
.login-form { width: 260px; }
.login-form input {
display: block; width: 100%; box-sizing: border-box;
margin-bottom: 10px; padding: 8px;
}
.btn-login { width: 100%; padding: 8px; cursor: pointer; }
.my-alert {
position: fixed; top: 20px; left: 50%; transform: translateX(-50%);
padding: 10px 24px; border-radius: 4px; color: #fff;
opacity: 0; transition: opacity .3s; pointer-events: none;
}
.my-alert.show { opacity: 1; }
.alert-success { background: #28a745; }
.alert-danger { background: #dc3545; }
</style>
</head>
<body>
<div class="my-alert"></div>
<form class="login-form">
<!-- name 的值 = 接口文档里的参数名,不能自己起 -->
<input type="text" name="username" placeholder="用户名">
<input type="password" name="password" placeholder="密码">
<!-- type="button":避免表单默认提交刷新页面 -->
<button type="button" class="btn-login">登录</button>
</form>
<script src="./lib/axios.js"></script>
<script src="./lib/form-serialize.js"></script>
<script>
// ⚠️ 唯一需要你填的地方:从接口文档「登录接口」复制请求地址
// 请求方法 POST、参数名 username/password、长度要求,也全部来自接口文档
const LOGIN_URL = 'https://xxx/api/login'
// 4. 封装提示框函数
function myAlert(msg, isSuccess) {
const alertDom = document.querySelector('.my-alert')
alertDom.classList.add('show')
alertDom.innerText = msg
alertDom.classList.add(isSuccess ? 'alert-success' : 'alert-danger')
setTimeout(() => {
alertDom.classList.remove('show')
alertDom.classList.remove(isSuccess ? 'alert-success' : 'alert-danger')
}, 1000)
}
// 1. 登录按钮 - 绑定点击事件
document.querySelector('.btn-login').addEventListener('click', () => {
if (LOGIN_URL.includes('xxx')) {
myAlert('请先把 LOGIN_URL 换成接口文档里的地址', false)
return
}
// 2. 用插件一次性收集表单里的用户名和密码(解构出同名变量)
const form = document.querySelector('.login-form')
const { username, password } = serialize(form, { hash: true, empty: true })
// 3. 判断长度:不符合要求就不发请求,return 阻断后续代码
if (username.length < 8) {
myAlert('用户名的长度应该大于等于 8 位', false)
return
}
if (password.length < 6) {
myAlert('密码的长度应该大于等于 6 位', false)
return
}
// 5. 基于 axios 提交用户名和密码
axios({
url: LOGIN_URL,
method: 'POST', // body 参数 → 用 data
data: { username, password } // axios 会自动转成 JSON
}).then(res => {
myAlert(res.data.message, true) // 成功:res.data.message
}).catch(err => {
myAlert(err.response.data.message, false) // 失败:err.response.data.message
})
})
</script>
</body>
</html>要点解释
url、method、参数名、长度要求(用户名 ≥ 8、密码 ≥ 6)全部来自接口文档,不是凭空写的;文档里参数位置是query就用params,是body就用data。data: { username, password }传的是普通对象,axios 会自动转成 JSON 并设置Content-Type: application/json,不需要手动JSON.stringify。const { username, password } = serialize(...):解构出与接口文档同名的变量,后面所有代码不用改。前提是empty: true,否则空输入时解构出来是undefined,username.length直接报错。- 两处
return是必须的:只写if不return,请求照样会发出去,校验等于没做。 - 成功取
res.data.message,失败取err.response.data.message,注意失败时多一层response。可以在浏览器 Network 面板里右键该属性「复制属性路径」来确认。 - 按钮写成
type="button",否则点击会触发表单默认提交、页面刷新,提示框根本看不到。 - 易错点:
name属性漏写或写错(比如写成uname),serialize收集出来的对象属性名就与接口文档对不上,服务器直接返回参数错误。 - 调试顺序建议:先
console.log(data)确认收集到的对象 → 再确认校验逻辑(Network 面板看请求有没有发出去)→ 最后看响应报文确认提示文案。
常见错误
现象:先登录失败(红框),再登录成功,弹框却仍是红色 / 两次提示样式串味。 原因:
myAlert里只在显示时classList.add成功/失败类名,setTimeout隐藏时只移除了show,没移除alert-success/alert-danger;两个类名共存时alert-danger优先级更高,最终显示为失败样式。 修法:隐藏时把当初加进去的类名一起移除——alertDom.classList.remove(flag ? 'alert-success' : 'alert-danger'),保证"加了什么就能减什么"。现象:用户名没填,点了登录,页面弹了提示,但网络面板里请求照样发出去了。 原因:
if (userName.length < 8) { myAlert(...) }里只弹提示,没打断后续流程,代码继续往下执行到axios。 修法:校验分支内加return,让不合法数据在提交前就终止。现象:
serialize(form, { hash: true, empty: true })拿到{ uname: '...', pword: '...' },提交给服务器报参数错误。 原因:input 的name属性随手写了,和接口文档里的参数名(username/password)不一致。 修法:name的值严格照接口文档的参数名填写——文档写什么就写什么,它不是给人看的,是给接口看的。现象:控制台报
serialize is not defined,或收集结果永远为空对象。 原因:form-serialize.js没引入 / 引入路径写错 / 放在自己的 js 之后加载;或参数 1 传的是某个 input、外层 div 而不是form标签本身,表单元素不在该 form 内。 修法:先引插件再引业务 js;给 form 加类名后用querySelector拿到 form 元素本身,确认要收集的输入框都写在<form>内部。现象:弹框第一次能弹,第二次不弹了;或者一进页面弹框就挂着不消失。 原因:
show类名在初始 HTML 里就写死了,或者在myAlert里重复querySelector拿到不同元素,导致remove和add没作用在同一个对象上。 修法:初始 HTML 不带show;弹框元素在函数外获取一次存变量,函数内统一操作这个变量。现象:接口文档写"最少 8 位",输入 8 位仍提示长度不够。 原因:边界写错,用了
length <= 8或length < 9之类,把合法值也拦住了。 修法:按文档语义翻译——"最少 8 位"即length < 8才报错,8本身应放行;改完用边界值 7 / 8 各测一次。
练习
改错:下面这段代码有两个问题,指出并修正。
jsdocument.querySelector('.btn-login').addEventListener('click', () => { const userName = document.querySelector('.username').value const password = document.querySelector('.password').value if (userName.length < 8) { myAlert('用户名长度应大于等于8', false) } if (password.length < 6) { myAlert('密码长度应大于等于6', false) } axios({ url: '...', method: 'post', data: { username: userName, password } }) .then(res => { myAlert(res.data.message, true) }) .catch(err => { myAlert(err.response.data.message, false) }) })实操:给
myAlert(msg, flag)补全"显示 → 改文案 → 加样式 → 延时消失 → 清样式"的完整逻辑,要求连续快速触发两次不同结果的提示(先失败后成功),最终弹框颜色必须与最后一次调用一致。实操:把第 1 题里手写的
userName/password取值,改成用form-serialize一次性收集,并保证后续axios及校验代码改动最小(提示:解构)。实操:接口文档中登录接口参数说明为「username:最少 8 位」「password:最少 6 位」。请为登录页补上两段校验,要求:不合法时不发请求;提示文案要能区分是用户名问题还是密码问题。
排查:页面点击登录后弹框一直不消失,且样式永远是红色。列出你的排查顺序(至少 3 步),并说明每步你会在哪里看什么。
原视频对照
| # | 视频 | 时长 |
|---|---|---|
| 1 | 接口文档 | 10:19 |
| 2 | 案例-用户登录01 | 13:37 |
| 3 | 案例-用户登录02 | 14:23 |
| 4 | form-serialize插件 | 09:00 |
| 5 | 案例-用户登录(整合form-serialize) | 06:47 |
小结
- 接口文档是唯一权威来源——字段名、请求方法、参数位置、长度限制,凡是从文档抄来的值都能对上,凡是自己"觉得合理"的值迟早出问题。
- 校验不通过就
return:前端校验的意义是"别把明显错误的数据发出去",而不是"发出去再让服务器骂回来"。 - 同一段逻辑出现 3 次以上就该抽函数;抽函数时先定参数(差异点),再写函数体(共性点),顺序反了会写成四份几乎一样的代码。
- 类名控制样式必须成对:
add了show就要能remove它,add了成功/失败样式也要能减掉,否则样式会"累积"成 bug。 - 插件的价值在于替换掉某一段重复劳动,而不改变整体结构——整合
form-serialize后只动了"取值"这一步,下游逻辑一行没改,这才是正确的接入方式。 name属性是表单和接口之间的契约,它把"页面上的输入框"和"服务器要的参数"绑定起来,所以它的值不能凭手感写。- 出问题先看事实:DOM 上到底挂了哪些类名、网络面板里请求到底发没发、响应体里到底返回了什么——比盯着 JS 逻辑猜快得多。