一、组件介绍
PasswordValidation 是一个基于 React 和 Ant Design 的通用密码强度校验与规则提示组件。它能够实时校验用户输入的密码是否符合企业安全要求,并以弹窗形式直观展示校验规则及当前状态。组件支持与 antd Form 无缝联动,便于在注册、修改密码等场景中复用。
二、使用场景

- 用户注册、修改密码、重置密码等需要密码输入的表单
- 需要统一密码强度策略的企业级后台系统
- 需要实时提示密码合规性的前端页面
- 需要与 Ant Design Form 组件集成的项目
三、核心思路
- 规则配置:将密码校验规则(长度、字符类型、多样性)抽象为常量配置,便于维护和扩展。
- 实时校验:每次输入变更时,自动校验所有规则,并将结果同步到表单校验状态。
- 规则提示:通过 Popover 弹窗,实时展示每条规则的通过情况,提升用户体验。
- 表单联动:支持与 antd Form 组件联动,自动设置字段校验状态和错误提示。
- ref 暴露:通过 ref 暴露校验方法,便于外部自定义校验逻辑。
四、关键代码实现
1. 规则配置与校验
js
const PASSWORD_RULES = {
length: {
min: 8,
max: 20,
test: (password: string) => password.length >= 8 && password.length <= 20
},
chars: {
pattern: /^[a-zA-Z0-9!"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~]+$/,
test: (password: string) => PASSWORD_RULES.chars.pattern.test(password) && !/\\s/.test(password)
},
variety: {
minTypes: 3,
test: (password: string) => {
let types = 0;
if (/[A-Z]/.test(password)) types++;
if (/[a-z]/.test(password)) types++;
if (/[0-9]/.test(password)) types++;
if (/[!"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~]/.test(password)) types++;
return types >= PASSWORD_RULES.variety.minTypes;
}
}
};
2. 核心实现
js
export const PasswordValidation = forwardRef<WithPasswordRulesRef, WithPasswordRulesProps>(({
children,
form,
value = '',
name,
trigger = 'hover',
placement = 'bottomLeft',
title,
...restProps
}, ref) => {
const validationsRef = useRef({ length: false, chars: false, variety: false });
const validatePassword = useCallback((password: string) => ({
length: PASSWORD_RULES.length.test(password),
chars: PASSWORD_RULES.chars.test(password),
variety: PASSWORD_RULES.variety.test(password)
}), []);
const updateFieldStatus = useCallback((isValid: boolean) => {
if (!name || !form) return;
if (!value) {
form.setFields([{ name, errors: ['密码不能为空'], validating: false, touched: true }]);
} else if (!isValid) {
form.setFields([{ name, errors: ['请按正确格式输入密 码'], validating: false, touched: true }]);
} else {
form.setFields([{ name, errors: undefined, validating: false }]);
}
}, [name, form, value]);
const passwordRulesContent = useMemo(() => {
const validations = validatePassword(value);
const rules = [
{ valid: validations.length, text: '8-20个字符', key: 'length' },
{ valid: validations.chars, text: '只能包含大小写字母、数字以及标点符号(除空格)', key: 'chars' },
{ valid: validations.variety, text: '大写字母、小写字母、数字和标点符号至少包含3种', key: 'variety' }
];
return (
<div className={styles.container}>
{rules.map(rule => (
<div className={styles.item} key={rule.key}>
<CheckCircleOutlined className={classnames(styles.icon, { [styles.success]: rule.valid })} />
<Text className={classnames(styles.text, { [styles.success]: rule.valid })}>{rule.text}</Text>
</div>
))}
</div>
);
}, [value, validatePassword]);
const handleValidator = useCallback(async () => {
const isValid = Object.values(validationsRef.current).every(valid => valid);
if (!isValid) {
return Promise.reject('请按正确格式输入密码');
}
return Promise.resolve();
}, []);
useEffect(() => {
validationsRef.current = validatePassword(value);
const isValid = Object.values(validationsRef.current).every(valid => valid);
updateFieldStatus(isValid);
}, [value, validatePassword, updateFieldStatus]);
// 处理子组件焦点
const handleChildFocus = useCallback((originalOnFocus?: (e: React.FocusEvent<HTMLInputElement>) => void) => {
return (e: React.FocusEvent<HTMLInputElement>) => {
originalOnFocus?.(e);
if (form) {
const isValid = Object.values(validationsRef.current).every(valid => valid);
updateFieldStatus(isValid);
}
};
}, [form, updateFieldStatus]);
// 克隆子组件
const childrenWithProps = useMemo(() => {
return React.cloneElement(children, {
onFocus: handleChildFocus(children.props.onFocus)
});
}, [children, handleChildFocus]);
useImperativeHandle(ref, () => ({
getValidations: () => validationsRef.current,
handleValidator
}), [handleValidator]);
return (
<Popover
content={passwordRulesContent}
trigger={trigger}
placement={placement}
title={title || '密码需满足以下要求'}
overlayClassName={styles.password_popover}
getPopupContainer={triggerNode => triggerNode.parentNode as HTMLElement}
{...restProps}
>
{childrenWithProps}
</Popover>
);
});
3. 基础使用
js
<Form.Item
label="设置密码"
name="password"
rules={[
{ required: true, message: '请输入密码' },
{
validator: async (_, value) => {
if (passwordRef.current) {
await passwordRef.current.handleValidator();
}
}
}
]}
>
<PasswordValidation ref={passwordRef} form={form}>
<Input.Password placeholder="请输入密码" />
</PasswordValidation>
</Form.Item>
五、注意事项
- 与 antd Form 联动时,必须传入 form 和 name 属性,否则无法自动设置表单校验状态。
- 建议外部受控 value,组件内部不维护 value 状态。
- Popover 默认 hover 触发,可通过 trigger 属性自定义。
- 如需多语言或自定义文案,可自行扩展。
- 如需自定义样式,可通过 overlayClassName 传递 className。
- 如需自定义校验规则,可修改 PASSWORD_RULES 配置。
六、总结
PasswordValidation 组件极大简化了密码强度校验和规则提示的开发工作,支持灵活扩展和与 antd Form 的无缝集成。它不仅提升了用户体验,也保证了密码安全策略的统一落地。推荐在所有涉及密码输入的场景中使用。如需多语言、弱密码黑名单等高级功能,可在此基础上二次开发。