鸿蒙 ArkTS 表单校验实战:用 @hmkit/validator 管理字段规则与异步校验
在鸿蒙应用开发中,注册、资料编辑、企业认证等页面,通常都需要处理表单校验。
最开始,可能只是判断必填和手机号格式。随着业务增加,还会出现密码确认、条件必填、用户名查重,以及编辑页面只校验部分字段等需求。
如果这些逻辑都写在页面事件里,规则、错误提示和异步状态就容易混在一起,后续修改也需要反复检查多个入口。
我一直在维护的开源组件 **`@hmkit/validator`**,就是希望把这些校验规则整理成可复用的 Schema,让页面更专注于输入、展示和业务交互。
这篇文章结合几个常见场景,介绍它的接入方式和使用边界。
## 一、@hmkit/validator 能做什么
`@hmkit/validator` 是使用 ArkTS 实现的声明式校验库,核心 HAR 没有第三方运行时依赖。
目前提供的能力包括:
- 字符串、数字、布尔、日期、对象和数组校验;
- 手机号、身份证、银行卡、车牌、统一社会信用代码等业务规则;
- 跨字段校验、条件必填、默认值和数据转换;
- 异步校验,以及 FormValidator 的防抖、状态管理、取消和超时;
- Schema 组合、递归结构、JSON Schema 子集转换等进阶能力。
规则可以独立用于数据校验,也可以通过 FormValidator 与 ArkUI 表单交互连接起来。
## 二、安装并校验一个注册表单
在需要使用的模块中安装依赖:
```bash
ohpm install @hmkit/validator
```
下文基础示例使用 1.3 系列已有的 API。后面的 SchemaFactory 示例需要包含该入口的 1.4.0 版本。
OHPM 的可安装版本以中心仓页面为准,也可以从仓库 Release 获取对应 HAR:
- [OHPM 包页面](https://ohpm.openharmony.cn/#/cn/detail/@hmkit%2Fvalidator)
- [GitHub Releases](https://github.com/lxshwyan/harmony-validator/releases)
假设注册页面需要校验用户名、手机号、密码和确认密码,可以把规则集中定义:
```typescript
import { v } from '@hmkit/validator';
const registerSchema = v.object({
'username': v.string()
.required('请输入用户名')
.min(3),
'phone': v.string()
.required('请输入手机号')
.phone(),
'password': v.string()
.required('请输入密码')
.min(8),
'confirmPassword': v.string()
.required('请再次输入密码')
}).refine(
(data: Record<string, Object>): boolean =>
data['password'] === data['confirmPassword'],
'两次输入的密码不一致',
'confirmPassword'
);
const values: Record<string, Object> = {
'username': 'Alice',
'phone': '13800138000',
'password': 'abc12345',
'confirmPassword': 'abc12346'
};
const result = registerSchema.validate(values);
if (!result.valid) {
for (const error of result.errors) {
console.info(`${error.path}: ${error.message}`);
}
}
```
这个例子中,两次密码不同,错误会关联到 `confirmPassword` 字段:
```text
confirmPassword: 两次输入的密码不一致
```
页面可以根据错误的 `path` 显示对应提示。规则也可以放到独立文件中,由不同页面或业务流程复用。
需要注意:**字符串默认允许留空,必须填写的字段应显式调用 `.required()`。**
## 三、处理条件必填和数据转换
### 1. 根据其他字段决定是否必填
有些字段是否必填,取决于用户选择。
例如,选择企业身份时需要填写税号,个人身份则允许留空:
```typescript
import { v } from '@hmkit/validator';
const identitySchema = v.object({
'identity': v.string().required(),
'taxId': v.string().requiredWhen(
'identity',
(value: Object | null | undefined): boolean =>
value === 'company',
'企业身份需要填写税号'
)
});
```
这样,条件规则可以与字段定义放在一起维护。
这里的规则只决定税号是否必填,格式、长度和业务有效性仍应根据实际要求补充。
### 2. 通过 parse 取得转换结果
有时除了校验,还需要处理输入数据。例如,将邀请码统一转换成大写:
```typescript
import { v } from '@hmkit/validator';
const inviteCode = v.string()
.required()
.transform<string>(
(value: string): string => value.toUpperCase()
);
const result = inviteCode.parse('vip2026');
if (result.success) {
console.info(result.value); // VIP2026
}
```
需要取得转换后的数据时,应读取 `parse()` 或 `parseAsync()` 的结果。
`validate()` 返回的是校验结果,调用之后不应把原始输入当作已经完成转换的数据继续使用。
## 四、异步查重需要同时管理请求和状态
用户名查重看起来只是调用一次接口,但输入过程中还可能发生:
- 前一次查询未结束,用户已经输入了新内容;
- 较慢的旧结果晚于新结果返回;
- 用户离开页面时,请求仍在进行;
- 校验超时后,底层请求仍占用资源。
库通过 `customAsync()` 接入异步规则,FormValidator 负责字段交互和校验状态。
下面先用定时器模拟查重,展示异步规则和取消通知的连接方式。这段代码没有访问真实服务:
```typescript
import { v } from '@hmkit/validator';
import { FormValidator, FormTrigger } from '@hmkit/validator/form';
import { ValidationOptions } from '@hmkit/validator/execution';
function mockCheckUsername(
value: string,
options?: ValidationOptions
): Promise<boolean> {
return new Promise<boolean>((resolve): void => {
let unsubscribe: (() => void) | undefined;
const timer = setTimeout((): void => {
unsubscribe?.();
resolve(value !== 'taken');
}, 300);
unsubscribe = options?.cancellation?.onCancel((): void => {
clearTimeout(timer);
resolve(false);
});
});
}
```
然后将规则交给 FormValidator:
```typescript
async function runUsernameDemo(): Promise<void> {
const form = new FormValidator({
'username': v.string()
.required('请输入用户名')
.min(3)
.customAsync(mockCheckUsername, '用户名已被占用')
}, {
triggers: [FormTrigger.CHANGE],
debounceMs: 300,
timeoutMs: 2000
});
try {
const values: Record<string, Object> = {
'username': 'Alice'
};
// 对应输入变化事件
await form.onChange('username', 'Alice', values);
// 对应提交事件,校验通过后才执行处理器
const errors = await form.submit(
values,
async (_data: Record<string, Object>): Promise<void> => {
console.info('表单校验通过');
}
);
console.info(JSON.stringify(errors));
} finally {
form.dispose();
}
}
```
在实际页面中,可以将不同事件连接到对应入口:
| 页面交互 | 对应方法 |
| --- | --- |
| 输入内容变化 | `onChange()` |
| 输入框失焦 | `onBlur()` |
| 提交表单 | `submit()` |
| 重置表单状态 | `reset()` |
| 销毁控制器 | `dispose()` |
控制器还提供 `touched`、`dirty`、`validating` 和 `submitting` 等状态,页面可以据此更新提示和按钮状态。
同步 `validate()` 不执行异步规则。包含查重等异步规则时,需要使用异步校验入口。
### 接入真实请求时的注意事项
**取消通知需要由网络适配器主动处理。**
接入真实 HTTP 请求时,应订阅取消通知,并调用底层网络 API 取消请求、释放资源。仅让校验提前返回,并不能证明请求已经停止传输。
页面也需要根据隐藏、退出和销毁等生命周期清理校验任务。已经 dispose 的控制器,应重新创建后再使用。
仓库提供了可参考的 RCP 请求适配器:
[查看 HttpValidation.ets](https://github.com/lxshwyan/harmony-validator/blob/v1.4.0/example/HttpValidation.ets)
这个适配器是 HarmonyOS 示例,实际项目可以按照同一取消协议接入自己的网络层。
## 五、复用 Schema 时,注意实例共享
把规则集中管理以后,很容易写出下面这样的代码:
```typescript
const nameRule = v.string().required();
const shortName = nameRule.max(8);
const longName = nameRule.max(32);
```
基础链式方法会修改当前实例,这里的变量仍然引用同一个规则对象,不能据此得到两份独立规则。
需要独立规则时,可以使用每次都返回新实例的工厂函数。
1.4.0 还提供了 `SchemaFactory`,用于组织构造和派生:
```typescript
import { liteV, StringSchema } from '@hmkit/validator/lite';
import { SchemaFactory } from '@hmkit/validator/reuse';
const names = new SchemaFactory<StringSchema>(
(): StringSchema => liteV.string().required()
);
const shortNames = names.derive(
(schema: StringSchema): StringSchema => schema.max(8)
);
const longNames = names.derive(
(schema: StringSchema): StringSchema => schema.max(32)
);
shortNames.create().validate('123456789'); // 不通过
longNames.create().validate('123456789'); // 通过
```
每次 `create()` 都重新执行构造函数,再应用派生配置。
因此,可变的子 Schema 也需要放在构造函数内部创建。如果构造函数始终返回外部共享实例,或者引用外部已有的子规则,这些引用仍然会共享。
## 六、接入前需要了解的几个边界
实际选型和接入时,除了 API 是否够用,还需要了解默认行为。
### 对象额外字段
对象的未知字段默认保留。
需要拒绝额外字段时,可以使用 `.strict()`;需要在解析输出中删除额外字段时,可以使用 `.strip()`。
### 显式模型与规则一致性
`v.object<Model>()` 保留显式结果类型,Model 与字段规则之间的一致性仍需要维护。
仓库提供了模型和 Schema 的单一来源生成工具,用于减少重复定义,但它只覆盖文档列出的生成子集。
### JSON Schema 支持范围
JSON Schema 导入和导出有明确的支持范围。
使用导入能力时,可以结合严格模式和诊断接口,检查当前未支持的约束,避免忽略规则差异。
### 仓库工具与 HAR
模型生成器、JS/TS Standard Schema 桥接和网络示例通过源码仓库提供,不随 HAR 安装。
其中,JS/TS 标准桥接用于对应运行环境的互操作,不代表 ArkTS 自动获得 TypeScript 的类型推导能力。
## 七、验证情况与开源地址
1.4.0 的验证记录包括:
- 299 项库回归测试;
- API12、API23 两组目标下的主入口和按需入口消费编译;
- API23 模拟器中的重启、取消、恢复和页面退出重进;
- 真实 HTTP 场景中,两种入口共 12 个在途取消请求的服务端断连确认。
目前还没有真机验证结果。实际项目采用的 SDK、网络适配器和页面生命周期,仍需要在目标环境中完成接入验证。
详细记录可以查看:
[1.4.0 验证报告](https://github.com/lxshwyan/harmony-validator/blob/master/doc/VALIDATION-1.4.0.md)
如果你正在开发鸿蒙注册表单、资料编辑页或业务数据录入功能,可以先从一个字段或一个表单开始接入。
项目地址:
码云: https://gitee.com/lxsh_wyan/harmony-validator
https://github.com/lxshwyan/harmony-validator
https://ohpm.openharmony.cn/#/cn/detail/@hmkit%2Fvalidator
欢迎反馈实际使用中的问题。提交 Issue 时,附上 SDK 版本、最小示例、输入数据和预期结果,会更方便复现和修复。
后续我也会根据具体业务需求,继续完善规则复用、文档和工程接入体验。
更多推荐



所有评论(0)