鸿蒙(HarmonyOS NEXT)表单校验别再手撸正则了 —— 我写了个 ArkTS 版 zod

鸿蒙(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 向后兼容,异步规则按需启用。

链接

如果你也在鸿蒙上写表单,不妨试试,少撸几行正则。觉得有用点个 star,有需求提 issue,我会持续迭代 🙌

相关推荐
贾伟康3 分钟前
【时光清单|11】HarmonyOS ArkTS 每日语录实战:从本地仓库稳定生成首页内容
harmonyos·arkts·状态管理·arkui·本地数据
tsqtsqtsq030913 分钟前
鸿蒙系统深色模式功能详解与开发适配指南
harmonyos
贾伟康44 分钟前
【时光清单|16】HarmonyOS ArkTS 多设备布局实战:适配手机、平板和 PC/2in1 的窗口变化
harmonyos·arkts·arkui·响应式布局·多设备适配
lilian2339 小时前
HarmonyOS 7 新特性(二十五)|智慧手势:意图识别与误触治理
华为·harmonyos
贾伟康11 小时前
【时光清单|14】HarmonyOS ArkTS 主导航实战:统一页面入口、返回路径和参数校验
harmonyos·arkts·arkui·navigation·路由管理
贾伟康16 小时前
【时光清单|05】HarmonyOS ArkTS 倒计时卡片实战:适配 2x2、2x4 与 4x4 多尺寸
harmonyos·arkts·arkui·服务卡片·formextensionability
小雨青年16 小时前
【HarmonyOS 7 悬浮页签深度实战】07 折叠屏、平板与宽窗口的布局适配
华为·harmonyos
熊猫钓鱼>_>18 小时前
鸿蒙AI Agent新范式:从“对话式辅助”到“工程化代理”的Harness架构实战解析
人工智能·笔记·学习·华为·架构·harmonyos
大锅盖118 小时前
HarmonyOS ArkUI 非遗纹样素材平台设计复盘:朱砂宣纸与鎏金黛蓝的文化守护
华为·harmonyos
2501_9197490319 小时前
华为鸿蒙管理学习生活与工作APP—小羊管理
学习·华为·生活·harmonyos·鸿蒙