You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue3自定义组件中ref与v-for结合及表单聚焦、数据更新问题

Vue3 开发问题解决方案

1. 自定义组件中同时使用 ref 和 v-for

在 v-for 中给自定义组件绑定 ref,需要通过函数式 ref收集实例,避免直接绑定变量导致的覆盖问题。同时组件内部要通过 defineExpose 暴露需要访问的属性或方法。

示例代码:

<template>
  <div>
    <CustomComponent
      v-for="item in list"
      :key="item.id"
      :ref="el => { if (el) componentRefs.push(el) }"
      :data="item"
    />
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import CustomComponent from './CustomComponent.vue'

const list = ref([{ id: 1 }, { id: 2 }, { id: 3 }])
const componentRefs = ref([])

onMounted(() => {
  // 访问第一个组件实例
  console.log(componentRefs.value[0])
})
</script>

自定义组件 CustomComponent.vue 需暴露内部内容:

<script setup>
import { ref } from 'vue'
const inputRef = ref(null)

// 暴露给父组件访问
defineExpose({
  inputRef,
  focus: () => inputRef.value.focus()
})
</script>

2. 提交时聚焦第一个错误输入框 + 父组件访问子组件元素

核心思路:子组件暴露内部输入框的 ref 和聚焦方法,父组件收集所有子组件实例,验证后找到第一个错误项并调用聚焦方法。

步骤1:子组件 CustomInput.vue 暴露输入元素

<template>
  <input
    ref="inputRef"
    v-model="localValue"
    :class="{ 'error': !field.isValid }"
  />
</template>

<script setup>
import { ref, computed } from 'vue'
const props = defineProps(['field'])
const emit = defineEmits(['update:field'])

const inputRef = ref(null)
const localValue = computed({
  get: () => props.field.value,
  set: val => emit('update:field', { ...props.field, value: val })
})

// 暴露输入框ref和聚焦方法
defineExpose({
  inputRef,
  focus: () => inputRef.value?.focus(),
  get isValid() {
    return props.field.isValid
  }
})
</script>

步骤2:父组件 CustomForm.vue 收集实例并处理提交

<template>
  <form @submit.prevent="handleSubmit">
    <CustomInput
      v-for="field in formFields"
      :key="field.name"
      :ref="el => { if (el) inputRefs.push(el) }"
      :field="field"
      @update:field="updateField"
    />
    <button type="submit">提交</button>
  </form>
</template>

<script setup>
import { ref } from 'vue'
import CustomInput from './CustomInput.vue'

const formFields = ref([
  { name: 'username', value: '', isValid: true, error: '' },
  { name: 'email', value: '', isValid: true, error: '' }
])
const inputRefs = ref([])

const updateField = (updatedField) => {
  const index = formFields.value.findIndex(f => f.name === updatedField.name)
  if (index !== -1) {
    formFields.value[index] = updatedField
  }
}

const validateFields = () => {
  // 模拟验证逻辑
  formFields.value.forEach(field => {
    if (!field.value.trim()) {
      field.isValid = false
      field.error = '不能为空'
    } else {
      field.isValid = true
      field.error = ''
    }
  })
  // 返回是否全部有效
  return formFields.value.every(f => f.isValid)
}

const handleSubmit = () => {
  const isAllValid = validateFields()
  if (!isAllValid) {
    // 找到第一个错误的输入框并聚焦
    const firstErrorInput = inputRefs.value.find(input => !input.isValid)
    firstErrorInput?.focus()
  } else {
    // 提交逻辑
    console.log('表单提交')
  }
}
</script>

3. 从 Hooks 访问表单字段并修改主数组数据

将响应式的表单字段数组传入 Hooks,直接在 Hooks 中修改数组元素的属性(ref 包裹的数组是响应式的,修改内部元素会触发更新)。

定义自定义 Hook useFormValidation.js

import { watch } from 'vue'

export function useFormValidation(fieldsRef) {
  // 监听字段变化自动验证
  watch(
    () => fieldsRef.value.map(f => f.value),
    () => validateAllFields(),
    { deep: true }
  )

  // 手动触发全量验证
  const validateAllFields = () => {
    fieldsRef.value.forEach(field => {
      if (!field.value.trim()) {
        field.isValid = false
        field.error = '不能为空'
      } else if (field.name === 'email' && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(field.value)) {
        field.isValid = false
        field.error = '邮箱格式错误'
      } else {
        field.isValid = true
        field.error = ''
      }
    })
    return fieldsRef.value.every(f => f.isValid)
  }

  // 单个字段验证
  const validateField = (fieldName) => {
    const field = fieldsRef.value.find(f => f.name === fieldName)
    if (!field) return
    if (!field.value.trim()) {
      field.isValid = false
      field.error = '不能为空'
    } else {
      field.isValid = true
      field.error = ''
    }
  }

  return {
    validateAllFields,
    validateField
  }
}

在组件中使用 Hook

<script setup>
import { ref } from 'vue'
import { useFormValidation } from './useFormValidation.js'

const formFields = ref([
  { name: 'username', value: '', isValid: true, error: '' },
  { name: 'email', value: '', isValid: true, error: '' }
])

const { validateAllFields, validateField } = useFormValidation(formFields)

const handleSubmit = () => {
  const isAllValid = validateAllFields()
  if (isAllValid) {
    console.log('提交表单')
  }
}
</script>

内容的提问来源于stack exchange,提问作者Славик Гусев

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 20:02:47