鸿蒙(HarmonyOS NEXT)表单校验别再手撸正则了 ------ 我写了个 ArkTS 版 zod
一个零依赖、纯 ArkTS 的声明式校验库,内置手机号/身份证/银行卡等中国本地化规则 ,还能跟 ArkUI 表单组件实时联动。
ohpm i @hmkit/validator即用。
先说痛点
在鸿蒙上写一个注册表单,你大概率写过这样的代码:
kotlin
// 校验手机号
if (!phone) {
this.phoneErr = '请输入手机号';
} else if (!/^1[3-9]\d{9}$/.test(phone)) {
this.phoneErr = '手机号格式不正确';
} else {
this.phoneErr = '';
}
// 校验身份证...校验位算法你还记得吗?
// 校验银行卡...Luhn 算法又得查一遍
// 校验年龄、邮箱、确认密码...一个表单 100 行 if 起步
问题很明显:
- 重复 :每个字段一坨
if/else,十个字段十坨。 - 易错:身份证最后一位校验码、银行卡 Luhn,自己撸十有八九写错。
- 难维护:校验逻辑散落在 UI 里,改个规则要翻半天。
前端有 zod/yup,后端有各种 validator,鸿蒙生态却一直缺一个顺手的 。于是我把这件事做了:@hmkit/validator。
Before / After
同样校验「手机号 + 身份证 + 年龄满 18」:
Before(手撸,~30 行)
typescript
let errors: string[] = [];
if (!phone) errors.push('请输入手机号');
else if (!/^1[3-9]\d{9}$/.test(phone)) errors.push('手机号格式不正确');
if (!idCard) errors.push('请输入身份证');
else if (!isValidIdCard(idCard)) errors.push('身份证不正确'); // isValidIdCard 还得自己写校验位
if (age === undefined) errors.push('请输入年龄');
else if (!Number.isInteger(age)) errors.push('年龄须为整数');
else if (age < 18) errors.push('需年满 18 岁');
// ...
After(链式声明,一目了然)
css
import { v } from '@hmkit/validator';
// 注意:ArkTS 严格模式要求 Record 字面量 key 加引号
const schema = v.object({
'phone': v.string().required('请输入手机号').phone(),
'idCard': v.string().required().idCard(),
'age': v.number().required().integer().min(18, '需年满 18 岁'),
});
const result = schema.validate({ 'phone': phone, 'idCard': idCard, 'age': age });
// result.errors: [{ path: 'phone', message: '请输入正确的手机号' }, ...]
校验逻辑变成声明,错误自带字段路径,读起来像说话。
一分钟上手
bash
ohpm install @hmkit/validator
lua
import { v } from '@hmkit/validator';
// 单值
v.string().required().phone().validate('13800138000'); // { valid: true, errors: [] }
// 自定义错误信息
v.number().min(18, '需年满 18 岁').validate(16);
// { valid: false, errors: [{ path: '', message: '需年满 18 岁' }] }
核心能力
🇨🇳 中国本地化规则,开箱即用
这是它和「移植个 zod」最不一样的地方 ------ 国内高频场景一个库全搞定,而且该有的校验算法都做对了:
| 规则 | 说明 |
|---|---|
phone() |
手机号 |
idCard() |
18 位身份证(校验出生日期 + 校验位) |
bankCard() |
银行卡(Luhn 算法) |
creditCode() |
统一社会信用代码(GB32100 校验位) |
plateNumber() |
车牌(含新能源) |
landline() postalCode() |
固话 / 邮编 |
chineseName() qq() wechat() |
中文姓名 / QQ / 微信号 |
vin() ipv4() url() email() |
车架号 / IPv4 / 网址 / 邮箱 |
身份证不光算校验位,还会拒掉 1900-02-31 这种假日期;信用代码也补了 mod-31 校验位 ------ 这些细节才是「能用在生产」和「demo」的区别。
🧩 数组、深层嵌套、跨字段
css
// 数组逐元素校验,错误路径带下标
v.array(v.string().phone()).validate(['13800138000', '123']);
// errors: [{ path: '1', message: '请输入正确的手机号' }]
// 对象 ↔ 数组任意层级嵌套,路径自动拼接成 items.0.name
v.object({
'items': v.array(v.object({ 'name': v.string().required('请输入商品名') })),
}).validate({ 'items': [{ 'name': '' }] });
// errors: [{ path: 'items.0.name', message: '请输入商品名' }]
// 跨字段:确认密码一致
v.object({ 'pwd': v.string().required(), 'confirm': v.string().required() })
.refine(o => o['pwd'] === o['confirm'], '两次密码不一致', 'confirm');
⏳ 异步校验(远程查重)
用户名/手机号是否已注册,直接接异步规则:
dart
const schema = v.string().required().customAsync(
async (name) => await api.isUsernameAvailable(name), // 返回 true 通过
'用户名已被占用',
);
await schema.validateAsync('taken'); // { valid: false, errors: [...] }
🎚️ 条件 / 可选
ini
// 企业(type=company)才必填税号
v.string().requiredWhen('type', t => t === 'company', '企业必须填写税号');
招牌:跟 ArkUI 表单实时联动
光有校验逻辑还不够,鸿蒙开发者最想要的是「输入时实时变红、提交时整体校验」。库里带了一个 FormValidator 控制器,跟 TextInput 一接就是完整体验:
typescript
import { v, FormValidator } from '@hmkit/validator';
@Entry
@Component
struct RegisterForm {
private validator: FormValidator = new FormValidator({
'username': v.string().required('请输入用户名').min(3, '用户名至少3位'),
'phone': v.string().required().phone(),
});
@State username: string = '';
@State errors: Record<string, string> = {};
build() {
Column() {
// 关键:text 用 $$ 双向绑定,输入不丢焦点
TextInput({ text: $$this.username, placeholder: '用户名' })
.onChange((val: string) => {
const msg = this.validator.validateField('username', val);
this.errors = msg === null ? {} as Record<string, string>
: { 'username': msg } as Record<string, string>;
})
if (this.errors['username'] !== undefined) {
Text(this.errors['username']).fontColor('#E64340')
}
Button('提交').onClick(() => {
const errs = this.validator.validateAll({ 'username': this.username });
this.errors = errs; // 提交时整体校验
})
}
}
}
输入时变红框、提交时整体校验,一套搞定。
为什么敢用在生产
- 零依赖、纯 ArkTS,只想要校验逻辑的人不被迫拉任何 UI 依赖。
- 190 项单元测试覆盖每个规则 + 边界 + 反例(假身份证日期、错校验位、空值、跨字段、异步...),全过。
- 校验算法对照国标实现(身份证 GB11643、信用代码 GB32100、银行卡 Luhn)。
- 同步 API 向后兼容,异步规则按需启用。
链接
- 📦 OHPM:
ohpm i @hmkit/validator - ⭐ GitHub(欢迎 star / issue / PR):github.com/lxshwyan/ha...
如果你也在鸿蒙上写表单,不妨试试,少撸几行正则。觉得有用点个 star,有需求提 issue,我会持续迭代 🙌