HarmonyOS ArkUI V2 实战:Schema 驱动表单、2in1 适配与实时通信

我做了三个纯血鸿蒙开源组件:从数据校验、动态表单到实时通信

编辑提示:本文计划在 @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 解耦,写法接近常见的链式校验库,同时补充了手机号、身份证、银行卡、车牌、统一社会信用代码等国内业务规则。

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 表单

安装:

ohpm install @hmkit/form

@hmkit/form 会通过 ^1.0.0 安装兼容的 @hmkit/validator。建议提交生成的 oh-package-lock.json5,让团队和 CI 使用一致的依赖版本。

一个最小页面只需要字段声明、受控 values 和 controller:

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、描述、默认值和枚举信息,表单可以推导出常见字段:

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.nameaddress.postcode
  • 条件显示、条件禁用和字段依赖;
  • Select、Radio、Checkbox 的异步选项、失败重试和竞态隔离;
  • 动态 FormArray 的增删、移动、稳定 key 和 min/max;
  • 分组、折叠区块和分步流程;
  • 错误摘要,以及跨步骤、跨折叠区块定位;
  • 自定义字段 renderer;
  • prefix、suffix、label、help、error、submit Builder 插槽;
  • light/dark 主题、动态字体和无障碍语义。

这些能力最容易互相干扰。例如点击错误摘要中的一项时,目标字段可能位于另一个步骤的折叠区块中。正确顺序应该是:切换步骤、展开区块、等待字段进入组件树,再聚焦字段。

@hmkit/formFormStructureController 单独管理 section 和 step,把结构状态与业务 values/errors 分开:

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

表单解决的是数据采集和提交,但订单状态、聊天、协同编辑、设备消息等场景还需要稳定的实时连接。

ohpm install @hmkit/ws

@hmkit/ws 的核心是 API 12+ 纯 ArkTS WebSocket 客户端,包括显式状态机、网络感知、自动重连、连接超时、串行发送、可定制心跳和有界离线队列。

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 也都是显式选择,不会因为安装核心包就自动引入。

三个组件组合起来,可以形成一条清晰的数据链路:

服务端/实时消息
      ↓
@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 之间的兼容差异。

开源组件是否有价值,最终不取决于功能列表有多长,而取决于它能否进入真实项目、暴露问题,再把这些问题变成可重复的测试。


平台发布素材

推荐标题

  1. 我做了三个纯血鸿蒙开源组件:从数据校验、动态表单到实时通信
  2. HarmonyOS ArkUI V2 实战:用 Schema 驱动复杂表单与 2in1 响应式布局
  3. 从 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 键鼠验收的完整做法。

推荐标签

HarmonyOSHarmonyOS NEXTOpenHarmonyArkTSArkUI V22in1表单校验WebSocket开源

发布前检查

posted @ 2026-09-03 15:33  lxsh_wyan  阅读(44)  评论(0)    收藏  举报