用 reduce 实现表单校验的核心是累积结构化错误结果:{ valid: false, errors: { username: "用户名不能为空", email: "邮箱格式不正确" } },每个字段校验函数返回 { passed, message },reduce 初始值为 { valid: true, errors: {} },逐个合并校验状态,valid 用 && 累积实现一票否决。

用 reduce 做表单校验,核心不是“累加数字”,而是“累积校验结果”——每次处理一个字段,把它的校验状态(通过/失败 + 错误信息)合并进一个统一的结果对象中。
校验目标:返回结构化的错误汇总
不返回布尔值,也不抛异常,而是返回类似这样的对象:
{ valid: false, errors: { username: "用户名不能为空", email: "邮箱格式不正确" } }这样上层能清晰知道哪些字段出错、错在哪,也方便渲染到对应输入框旁。
把每个字段的校验逻辑封装成独立函数
每个字段校验应是纯函数:接收字段值和原始数据(便于跨字段校验),返回 { passed: boolean, message?: string }。
- username:非空 + 长度 2–12
- email:非空 + 符合基础邮箱正则
-
passwordConfirm:需与
password字段值一致
例如:
const validateEmail = (val, data) =>
!val ? { passed: false, message: "邮箱不能为空" } :
/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(val) ? { passed: true } :
{ passed: false, message: "邮箱格式不正确" };
用 reduce 逐个执行校验并聚合结果
把字段名、值、校验函数三者映射为一个数组(如 Object.entries(formConfig)),再用 reduce 累积:
(acc, [field, validator]) => {
const { passed, message } = validator(formData[field], formData);
if (!passed) acc.errors[field] = message;
acc.valid = acc.valid && passed;
return acc;
},
{ valid: true, errors: {} }
);
注意:
- 初始值 { valid: true, errors: {} } 是关键起点;
- 每次只更新 errors 和 valid,不新建整个对象(性能友好);
- valid 用 && 累积,天然实现“一票否决”。
支持异步校验(如用户名是否已存在)
若某字段需发请求(如防重名校验),可先用 Promise.all 并行触发所有异步校验,再对结果数组 reduce 合并错误。或改用 async/await + for...of 更直观——但若坚持用 reduce,需配合 async reduce 辅助函数(本质是串行 await)。
不复杂但容易忽略:同步校验快而确定,异步校验要加 loading 状态、防重复提交、设超时,这些不属于 reduce 职责,应由外层协调。

















