fix(@vben-core/form-ui): restore lock screen form submission (#8195)
* fix(@vben-core/form-ui): avoid stale form context access * fix(@vben/playground): constrain date range codec fields * test(@vben-core/form-ui): harden benchmark fixtures * docs(@vben/docs): document form runtime and benchmarks * fix(@vben/docs): 明确 getValues/submit 返回值及 submit 参数类型
This commit is contained in:
@@ -269,6 +269,22 @@ const [Form, formApi] = useVbenForm({
|
||||
|
||||
<DemoPreview dir="demos/vben-form/value-format" />
|
||||
|
||||
## 性能基准
|
||||
|
||||
表单性能基准覆盖组件初始化、单字段与批量更新、重置、Zod 校验、动态 schema、字段联动、codec 编码与快照,以及数组字段编辑、增删和子 schema 更新。完整运行:
|
||||
|
||||
```bash
|
||||
pnpm test:benchmark
|
||||
```
|
||||
|
||||
只检查表单相关基准时,可以直接指定文件:
|
||||
|
||||
```bash
|
||||
pnpm exec vitest bench --run packages/@core/ui-kit/form-ui/__tests__/form-component-performance.benchmark.ts packages/@core/ui-kit/form-ui/__tests__/form-performance.benchmark.ts
|
||||
```
|
||||
|
||||
基准结果用于比较同一环境、同一场景在修改前后的相对变化,不应把单次运行的绝对耗时作为跨机器阈值。运行前应停止开发服务器等高 CPU 任务,并保持 Node.js 版本一致。benchmark 文件不会进入普通 `test:unit` 流程。
|
||||
|
||||
## 表单校验
|
||||
|
||||
表单校验是一个非常重要的功能,可以通过 `rules` 属性进行校验。
|
||||
@@ -320,7 +336,7 @@ const [Form, formApi] = useVbenForm({
|
||||
|
||||
### 类型传递与插槽
|
||||
|
||||
使用 `useVbenForm<TFormValues, TSubmitValues>` 分别声明组件表单值和提交值。schema、slots、`setValues`、`formApi.form.values` 使用 `TFormValues`;`getValues`、submit 和 `handleSubmit` 第一参数使用 `TSubmitValues`。两种结构相同时只传一个泛型即可。
|
||||
使用 `useVbenForm<TFormValues, TSubmitValues>` 分别声明组件表单值和提交值。schema、slots、`setValues`、`getRawValues()` 使用 `TFormValues`;`getValues()` 和 `submit()` 返回 `Promise<TSubmitValues>`,其中 `submit()` 只接收可选的原生 `Event`;`handleSubmit` 第一参数使用 `TSubmitValues`。两种结构相同时只传一个泛型即可。
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
@@ -450,6 +466,12 @@ const submitting = formApi.form.useSelector((state) => state.meta.submitting);
|
||||
| compact | 是否紧凑模式(忽略为校验信息所预留的空间) | `boolean` | false |
|
||||
| scrollToFirstError | 表单验证失败时是否自动滚动到第一个错误字段 | `boolean` | false |
|
||||
|
||||
::: warning formApi.form 的挂载时机
|
||||
|
||||
`formApi.form` 是 `<Form />` 挂载后注入的 `FormContextApi`。不要在调用 `useVbenForm` 时从第二个返回值中解构或缓存 `form`,否则会保留挂载前的空引用。业务操作优先使用 `formApi` 上会等待挂载的公开方法,例如 `getRawValues()`、`setFieldError()`、`setFieldValue()` 和 `validate()`;只有在已经挂载的表单上下文中才直接使用 `formApi.form` 的细粒度订阅方法。
|
||||
|
||||
:::
|
||||
|
||||
::: tip handleValuesChange
|
||||
|
||||
`handleValuesChange` 的第一个参数是未编码的只读 `TFormValues`,第二个参数是本次发生变化的 schema 字段名。第三个参数 `getFormattedValues` 是惰性函数:不调用就不会执行 codec 或旧格式化管道。
|
||||
|
||||
@@ -121,7 +121,8 @@ function handleValuesChange(
|
||||
}
|
||||
|
||||
async function syncPreviewValues(values?: Readonly<ValueFormatFormValues>) {
|
||||
liveValues.value = { ...(values ?? formApi.form?.values ?? {}) };
|
||||
const rawValues = values ?? (await formApi.getRawValues());
|
||||
liveValues.value = { ...rawValues };
|
||||
transformedValues.value = await formApi.getValues();
|
||||
}
|
||||
|
||||
@@ -152,7 +153,7 @@ onMounted(async () => {
|
||||
</Card>
|
||||
|
||||
<div class="grid gap-4 lg:grid-cols-2">
|
||||
<Card title="原始 form.values(组件值)">
|
||||
<Card title="getRawValues() 输出(组件值)">
|
||||
<pre class="bg-muted overflow-auto rounded-md p-4 text-sm">{{
|
||||
liveValuesPreview
|
||||
}}</pre>
|
||||
|
||||
@@ -207,7 +207,7 @@ Create the form through `useVbenForm`:
|
||||
|
||||
## Typed Values and Slots
|
||||
|
||||
Use `useVbenForm<TFormValues, TSubmitValues>` to declare component-facing form values and submission values separately. Schema, slots, selectors, and `setValues` use `TFormValues`; `getValues`, submit, and the first `handleSubmit` argument use `TSubmitValues`. Pass one generic when both shapes are identical.
|
||||
Use `useVbenForm<TFormValues, TSubmitValues>` to declare component-facing form values and submission values separately. Schema, slots, selectors, and `setValues` use `TFormValues`; `getValues()` and `submit()` return `Promise<TSubmitValues>`, while `submit()` only accepts an optional native `Event`; the first `handleSubmit` argument is `TSubmitValues`. Pass one generic when both shapes are identical.
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
@@ -281,6 +281,28 @@ const [Form, formApi] = useVbenForm({
|
||||
|
||||
`schema.valueFormat`, `fieldMappingTime`, and `arrayToStringFields` remain runtime-compatible but are deprecated. When a codec is configured it takes precedence and deprecated transforms are ignored.
|
||||
|
||||
## Performance Benchmarks
|
||||
|
||||
The form benchmarks cover component initialization, single-field and batch updates, reset, Zod validation, dynamic schemas, dependencies, codec encoding and snapshots, plus array editing, row mutations, and child-schema updates. Run the complete benchmark suite with:
|
||||
|
||||
```bash
|
||||
pnpm test:benchmark
|
||||
```
|
||||
|
||||
To run only the form benchmarks, pass both files explicitly:
|
||||
|
||||
```bash
|
||||
pnpm exec vitest bench --run packages/@core/ui-kit/form-ui/__tests__/form-component-performance.benchmark.ts packages/@core/ui-kit/form-ui/__tests__/form-performance.benchmark.ts
|
||||
```
|
||||
|
||||
Use benchmark results to compare relative changes on the same machine and runtime; do not treat one run's absolute timings as portable thresholds. Stop CPU-intensive development servers first and keep the Node.js version consistent. Benchmark files are not included in the regular `test:unit` command.
|
||||
|
||||
::: warning Mounted form context
|
||||
|
||||
`formApi.form` is the `FormContextApi` injected after `<Form />` mounts. Do not destructure or cache `form` from the second `useVbenForm` return value during setup, because that captures the pre-mount empty reference. Prefer mount-aware public methods such as `getRawValues()`, `setFieldError()`, `setFieldValue()`, and `validate()` for business actions. Access fine-grained subscription methods on `formApi.form` only from an already-mounted form context.
|
||||
|
||||
:::
|
||||
|
||||
## Key API Notes
|
||||
|
||||
- `useVbenForm` returns `[Form, formApi]`
|
||||
@@ -295,7 +317,7 @@ const [Form, formApi] = useVbenForm({
|
||||
- `handleSubmit(values, rawValues)` receives the formatted payload and its corresponding raw snapshot
|
||||
- `fieldMappingTime`, `arrayToStringFields`, and `schema.valueFormat` are deprecated compatibility options
|
||||
- `codec.encode` defines the `getValues()` payload and `codec.decode` powers complete `setSubmitValues()` fills
|
||||
- `formApi.form` is the stable `FormContextApi`; raw TanStack generics are intentionally not exposed
|
||||
- `formApi.form` exposes the mounted `FormContextApi`; do not destructure or cache it before `<Form />` mounts
|
||||
- prefer `formApi.form.useFieldValue`, `useFieldValues`, and `useFieldError` for fine-grained subscriptions; use `useValues` only when the whole form is required
|
||||
- `useSelector` remains the compatibility selector for combined `{ values, errors, meta }` state
|
||||
- legacy `setupVbenForm({ defineRules })` still works, warns once in development, and is silent in production; use `rules` for new code
|
||||
|
||||
Reference in New Issue
Block a user