我做了三个纯血鸿蒙开源组件:从数据校验、动态表单到实时通信
编辑提示:本文计划在
@hmkit/form@0.1.1可从 OHPM 查询并完成全新安装验证后公开。发布前请删除本提示,并再次核对文末版本表。
HarmonyOS NEXT 的应用生态正在快速发展,但真正进入业务开发后,我们依然会反复遇到几个基础问题:数据如何校验,复杂表单如何组织,实时连接如何保持稳定。
这些问题单独看都不新鲜,困难在于把它们做成符合 ArkTS 和 ArkUI V2 约束、能跨 HAR 使用、可以长期维护的基础组件。
因此,我陆续开源了三个 hmkit 组件:
@hmkit/validator:负责数据结构、业务规则和错误信息;@hmkit/form:负责 ArkUI V2 表单渲染、交互状态和复杂表单流程;@hmkit/ws:负责 WebSocket 生命周期、协议扩展和实时消息能力。
它们并不是一个"大而全"的框架。我的目标是把边界划清楚:数据可信交给 validator,交互组织交给 form,实时连接交给 ws。项目可以只使用其中一个,也可以按需要组合。
为什么先做 validator,再做 form
很多表单组件把校验规则直接写进 UI。简单页面没问题,但业务增长以后,很容易出现这些情况:
- 同一规则散落在页面、弹窗和提交接口前;
- 动态字段出现后,错误状态与显示状态不同步;
- 异步校验返回较晚,覆盖了用户刚输入的新值;
- 表单分步或折叠后,错误存在,但用户不知道字段在哪里。
@hmkit/validator 把 Schema 和 UI 解耦,写法接近常见的链式校验库,同时补充了手机号、身份证、银行卡、车牌、统一社会信用代码等国内业务规则。
ts
import { v } from '@hmkit/validator';
const userSchema = v.object({
'name': v.string().required('请输入姓名').min(2),
'phone': v.string().required('请输入手机号').phone(),
'age': v.number().required('请输入年龄').integer().min(18, '需年满 18 岁')
});
const result = userSchema.validate({
'name': '张',
'phone': '123',
'age': 16
});
校验层只关心数据、规则和字段路径,不关心 TextInput 应该放在哪一列,也不决定错误信息使用什么颜色。
到了 @hmkit/form,同一套 Schema 会继续作为字段校验来源。表单组件负责 values、errors、change、blur、submit、reset、异步状态和首错定位,不再复制规则。
一个最小的 ArkUI V2 表单
安装:
bash
ohpm install @hmkit/form
@hmkit/form 会通过 ^1.0.0 安装兼容的 @hmkit/validator。建议提交生成的 oh-package-lock.json5,让团队和 CI 使用一致的依赖版本。
一个最小页面只需要字段声明、受控 values 和 controller:
ts
import { v } from '@hmkit/validator';
import {
FormController,
FormFieldSpec,
FormFieldType,
HmFormView
} from '@hmkit/form';
@Entry
@ComponentV2
struct FormPage {
private readonly fields: FormFieldSpec[] = [
new FormFieldSpec(
'name',
FormFieldType.TEXT,
'姓名',
v.string().required('请输入姓名')
),
new FormFieldSpec(
'age',
FormFieldType.NUMBER,
'年龄',
v.number().min(18, '年龄不能小于 18 岁')
)
];
@Local values: Record<string, Object> = {};
private readonly controller: FormController =
new FormController(this.fields, this.values);
build() {
HmFormView({
fields: this.fields,
values: this.values!!,
controller: this.controller,
onSubmit: (values: Record<string, Object>): void => {
// 校验通过,调用业务接口。
},
onInvalid: (errors: Record<string, string>): void => {
// 校验失败,组件已经定位首个错误字段。
}
})
}
aboutToDisappear(): void {
this.controller.dispose();
}
}
这里保留了 ArkUI V2 的受控数据流。组件不会偷偷维护另一份业务值,宿主仍然拥有最终状态。
Schema 不仅能校验,也能安全推导 UI
如果 Schema 已经包含 label、描述、默认值和枚举信息,表单可以推导出常见字段:
ts
import { inferFormFields } from '@hmkit/form';
const inferred = inferFormFields({
name: v.string().required().label('姓名').describe('与证件保持一致'),
age: v.number().label('年龄'),
enabled: v.boolean().label('启用'),
role: v.enumOf(['admin', 'member']).label('角色'),
tags: v.array(v.enumOf(['frontend', 'backend']))
.label('方向')
.default(['frontend'])
}, {
name: { uiOptions: { span: 6 } },
age: { uiOptions: { span: 6 } }
});
private fields = inferred.fields;
@Local values: Record<string, Object> = inferred.getInitialValues();
这种推导是有边界的。string、number、boolean、date、字符串 enum 等明确类型可以自动映射;object、普通数组、transform 或混合 union 无法可靠决定 UI,必须显式指定字段类型。
我更愿意让组件在歧义处报错,也不希望它"猜一个看起来能用"的界面。
复杂表单不只是多几个输入框
真实业务表单通常还需要:
- 点路径嵌套值,例如
profile.name、address.postcode; - 条件显示、条件禁用和字段依赖;
- Select、Radio、Checkbox 的异步选项、失败重试和竞态隔离;
- 动态 FormArray 的增删、移动、稳定 key 和 min/max;
- 分组、折叠区块和分步流程;
- 错误摘要,以及跨步骤、跨折叠区块定位;
- 自定义字段 renderer;
- prefix、suffix、label、help、error、submit Builder 插槽;
- light/dark 主题、动态字体和无障碍语义。
这些能力最容易互相干扰。例如点击错误摘要中的一项时,目标字段可能位于另一个步骤的折叠区块中。正确顺序应该是:切换步骤、展开区块、等待字段进入组件树,再聚焦字段。
@hmkit/form 用 FormStructureController 单独管理 section 和 step,把结构状态与业务 values/errors 分开:
ts
private structure = new FormStructureController([
new FormSectionSpec('account', '账号资料', ['name'], {
collapsible: true
}),
new FormSectionSpec('company', '企业资料', ['company'])
], [
new FormStepSpec('account', '创建账号', ['account']),
new FormStepSpec('company', '企业认证', ['company'])
]);
HmFormView({
fields: this.fields,
values: this.values!!,
controller: this.controller,
structure: this.structure,
showErrorSummary: true
})
下一步只校验当前步骤的 active fields,最终提交再校验完整表单。折叠和步骤切换默认释放子树,需要保留临时 UI 状态的字段才显式开启 keepAlive。
2in1 支持不应该靠判断设备型号
@hmkit/form@0.1.1 正式把 2in1 加入模块设备声明,但布局没有写成"如果是电脑就双列"。
组件使用 12 列栅格,并以组件宽度而不是设备名称判断断点:
- 容器小于 600vp:字段统一按单列排列;
- 容器达到 600vp:根据字段
span进入双列或跨列布局; - 2in1 窗口或父容器缩窄:原地回到单列;
- 布局变化不改变字段 name、key、受控状态和首错定位目标。
这种方式同样适用于 tablet、foldable、分屏和窗口化场景。设备类型决定应用能否安装,容器宽度决定界面如何排布,两者职责不同。
为避免"配置里写了 2in1 就算支持",项目增加了 MateBook Pro 自动化验收,覆盖:
- HDC 设备类型确认为
2in1; - 宽容器下两个
span: 6字段位于同一行; - 窄容器预览下恢复单列;
- 恢复宽度后重新进入双列;
- 鼠标坐标点击可以聚焦输入框;
- Tab 可以把焦点移动到下一字段。
同时在 Pura 90 上重新跑完整 Showcase,确认新增的 2in1 演示没有破坏手机端长页面交互。
为什么还要做 @hmkit/ws
表单解决的是数据采集和提交,但订单状态、聊天、协同编辑、设备消息等场景还需要稳定的实时连接。
bash
ohpm install @hmkit/ws
@hmkit/ws 的核心是 API 12+ 纯 ArkTS WebSocket 客户端,包括显式状态机、网络感知、自动重连、连接超时、串行发送、可定制心跳和有界离线队列。
ts
import {
HmWebSocketClient,
HmWebSocketClientOptions,
NetworkKitWsTransportFactory,
TextHeartbeatStrategy
} from '@hmkit/ws';
const options = new HmWebSocketClientOptions();
options.heartbeat = new TextHeartbeatStrategy(
'PING', 'PONG', 15000, 5000, true
);
const client = HmWebSocketClient.withUrl(
'wss://example.com/realtime',
new NetworkKitWsTransportFactory(),
options
);
await client.connect();
await client.send('hello');
JSON、STOMP 1.2、Socket.IO v4、MQTT 3.1.1/5.0 和 IM toolkit 都建立在明确的扩展层上,不强行污染最基础的 WebSocket 客户端。文件持久化和 Native backend 也都是显式选择,不会因为安装核心包就自动引入。
三个组件组合起来,可以形成一条清晰的数据链路:
text
服务端/实时消息
↓
@hmkit/ws:连接、重连、协议和消息
↓
@hmkit/validator:解析、校验和业务规则
↓
@hmkit/form:编辑、错误呈现和提交
我更在意"可验证",而不只是"功能很多"
组件库最危险的状态,是 Demo 可以运行,但真实 HAR 消费方式没有测试。
目前 @hmkit/form 的发布流程会验证:
- 63 项 form 自动化测试;
@hmkit/validator@1.1.0的 232 项测试和覆盖率门槛;- release HAR 的元数据、公开声明、实现源码泄漏和依赖路径;
- 仓内 Demo、独立最小消费者、独立全功能 Showcase;
- validator 仓库中的真实注册表单试点;
- Pura 90 手机端流程;
- MateBook Pro 2in1 响应式布局、鼠标和键盘焦点;
- API 12 最低基线和公开 API 冻结。
这里仍然存在工具链 warning:OHPM 生成的部分依赖 .d.ets 会带有严格类型检查提示。项目没有修改构建产物去掩盖 warning,而是把源码问题与生成器问题分开记录。
当前版本与链接
| 组件 | 版本 | 最低 API | 地址 |
|---|---|---|---|
@hmkit/validator |
1.1.0 | 12 | OHPM · GitHub |
@hmkit/form |
0.1.1 | 12 | OHPM · GitHub |
@hmkit/ws |
0.1.1 | 12 | OHPM · GitHub |
这些项目都采用 MIT License。0.x 版本仍处在快速迭代阶段,适合先在真实项目的局部页面或非核心链路试用,并锁定依赖版本。
如果你正在做 HarmonyOS NEXT 项目,欢迎提交 Issue,尤其希望收到这些反馈:
- ArkUI V2 跨 HAR 使用中遇到的限制;
- 真实业务缺少的字段类型和校验规则;
- tablet、foldable、2in1 和分屏场景的问题;
- WebSocket、MQTT、Socket.IO 或 IM 协议互操作案例;
- API 12 到新版本 SDK 之间的兼容差异。
开源组件是否有价值,最终不取决于功能列表有多长,而取决于它能否进入真实项目、暴露问题,再把这些问题变成可重复的测试。
平台发布素材
推荐标题
- 我做了三个纯血鸿蒙开源组件:从数据校验、动态表单到实时通信
- HarmonyOS ArkUI V2 实战:用 Schema 驱动复杂表单与 2in1 响应式布局
- 从 validator 到 form:如何为鸿蒙项目搭建可验证的表单基础设施
华为开发者社区摘要
本文介绍 hmkit 三个 HarmonyOS NEXT 开源组件:@hmkit/validator、@hmkit/form 和 @hmkit/ws。重点分享 ArkUI V2 声明式表单如何复用 Schema、处理动态字段、异步校验、分组/折叠/步骤、错误定位,以及如何用组件宽度断点正式适配 2in1。文章同时说明独立 HAR 消费、Pura 90 和 MateBook Pro 自动化验收的工程实践。
掘金/CSDN 摘要
在 HarmonyOS NEXT 项目里,表单、数据校验和实时通信很容易各自形成一套状态。本文通过三个纯 ArkTS 开源组件,讲清楚如何划分 validator、form、ws 的职责,并展示 Schema 推导 UI、复杂表单结构、600vp 响应式栅格和 2in1 键鼠验收的完整做法。
推荐标签
HarmonyOS、HarmonyOS NEXT、OpenHarmony、ArkTS、ArkUI V2、2in1、表单校验、WebSocket、开源
发布前检查
- OHPM 可以查询
@hmkit/form@0.1.1,且latest指向 0.1.1。 - 从空目录执行
ohpm install @hmkit/form@0.1.1成功。 - Registry HAR 中的
deviceTypes包含phone、tablet、2in1。 - README 首页不再写"0.1.1 正在准备"。
- GitHub 创建并推送
v0.1.1标签。 - 文中三个 OHPM 和 GitHub 链接可匿名打开。
- 删除文章顶部的编辑提示。