Skip to content

perf(VForm): avoid eager .value reads in default slot props#22846

Merged
J-Sek merged 1 commit into
masterfrom
fix/form-eager-slot-props
May 23, 2026
Merged

perf(VForm): avoid eager .value reads in default slot props#22846
J-Sek merged 1 commit into
masterfrom
fix/form-eager-slot-props

Conversation

@J-Sek

@J-Sek J-Sek commented May 11, 2026

Copy link
Copy Markdown
Contributor

fixes #22816

Note: I am considering shipping to dev instead.

Markup:

<template>
  <v-sheet class="mx-auto pa-4" style="max-width: 1200px">
    <h2 class="mb-2">VForm slot-prop perf — no slot usage</h2>
    <p class="text-medium-emphasis mb-4">
      Empty default slot. VForm must not re-render on field validation.
    </p>
    <v-form ref="plainForm">
      <v-container class="bg-surface-variant mb-2">
        <v-row v-for="r in valueCount" :key="r" style="height: 80px">
          <v-col v-for="n in columnCount" :key="n">
            <v-text-field :rules="rules" density="compact" hide-details="auto" />
          </v-col>
        </v-row>
      </v-container>
    </v-form>
    <v-btn class="mt-2" color="primary" block @click="run('plain')">
      Validate ({{ valueCount * columnCount }} fields)
    </v-btn>
    <div class="mt-2">Last validation: <strong>{{ plainTime.toFixed(1) }} ms</strong></div>

    <v-divider class="my-8" />

    <h2 class="mb-2">VForm slot-prop perf — slot consumes all values</h2>
    <p class="text-medium-emphasis mb-4">
      Slot destructures every reactive value via getters. Only the slot’s render
      tracks the deps — VForm itself still doesn’t re-render on every field change.
    </p>
    <v-form ref="slotForm">
      <template #default="{ ...formProps }">
        <v-container class="bg-surface-variant mb-2">
          <v-row v-for="r in valueCount" :key="r" style="height: 80px">
            <v-col v-for="n in columnCount" :key="n">
              <v-text-field :rules="rules" density="compact" hide-details="auto" />
            </v-col>
          </v-row>
        </v-container>
        <v-table class="mb-2" density="compact">
          <tbody>
            <tr><td>isValid</td><td><strong>{{ String(formProps.isValid) }}</strong></td></tr>
            <tr><td>isValidating</td><td><strong>{{ String(formProps.isValidating) }}</strong></td></tr>
            <tr><td>isDisabled</td><td><strong>{{ String(formProps.isDisabled) }}</strong></td></tr>
            <tr><td>isReadonly</td><td><strong>{{ String(formProps.isReadonly) }}</strong></td></tr>
            <tr><td>items.length</td><td><strong>{{ formProps.items.length }}</strong></td></tr>
            <tr><td>errors.length</td><td><strong>{{ formProps.errors.length }}</strong></td></tr>
          </tbody>
        </v-table>
      </template>
    </v-form>
    <v-btn class="mt-2" color="primary" block @click="run('slot')">
      Validate ({{ valueCount * columnCount }} fields)
    </v-btn>
    <div class="mt-2">Last validation: <strong>{{ slotTime.toFixed(1) }} ms</strong></div>

    <v-divider class="my-8" />

    <h2 class="mb-2">Run history</h2>
    <v-table density="compact">
      <thead>
        <tr><th>Variant</th><th>Time (ms)</th></tr>
      </thead>
      <tbody>
        <tr v-for="(r, i) in history" :key="i">
          <td>{{ r.variant }}</td>
          <td>{{ r.time.toFixed(1) }}</td>
        </tr>
      </tbody>
    </v-table>
  </v-sheet>
</template>

<script lang="ts" setup>
  import { ref } from 'vue'

  const plainForm = ref()
  const slotForm = ref()
  const plainTime = ref(0)
  const slotTime = ref(0)
  const history = ref<{ variant: string, time: number }[]>([])

  const valueCount = 5
  const columnCount = 14

  const required = (v: unknown) => !!v || 'Required.'
  const rules = [required]

  async function run (variant: 'plain' | 'slot') {
    const target = variant === 'plain' ? plainForm : slotForm
    const start = performance.now()
    await target.value?.validate()
    const time = performance.now() - start
    if (variant === 'plain') plainTime.value = time
    else slotTime.value = time
    history.value.unshift({ variant, time })
  }
</script>

@J-Sek J-Sek requested a review from KaelWD May 11, 2026 06:51
@J-Sek J-Sek self-assigned this May 11, 2026
@J-Sek J-Sek added C: VForm performance The issue involves performance labels May 11, 2026
@J-Sek J-Sek merged commit 4369608 into master May 23, 2026
17 checks passed
@J-Sek J-Sek deleted the fix/form-eager-slot-props branch May 23, 2026 20:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

C: VForm performance The issue involves performance

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug Report][4.0.6] Performance issue for data table within forms and validation

1 participant