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

Vue 2.7 Composition API自定义组件v-model绑定异常求助

问题描述

使用Vue 2.7结合Composition API开发自定义输入组件,父组件通过v-model绑定响应式对象form.address.complement,但Vue DevTools中显示组件的modelValue为undefined,$attrs中存在对应值,双向绑定未正常生效。

组件代码:

<template>
  <div class="ui-field">
    <label :for="id">{{ label }}{{ required ? '*' : '' }}</label>
    <input
      :aria-errormessage="errorDescriptionId()"
      :aria-invalid="!!error ? 'true' : 'false'"
      :autocomplete="autocomplete"
      class="ui-input"
      :data-acting="is_acting"
      :disabled="disabled"
      :id="id"
      :required="required"
      :type="type"
      :value="modelValue"
      @input="emitChange"
    />
    <p :id="errorDescriptionId()" v-if="error">{{ error }}</p>
  </div>
</template>

<script lang="ts" setup>
type UIInput = {
  autocomplete?: 'on' | 'off' | undefined
  disabled?: boolean | undefined
  error?: string | undefined
  id?: string | undefined
  is_acting?: boolean | undefined
  label?: string | undefined
  required?: boolean | undefined
  type?: 'text' | 'number' | undefined
  modelValue?: any
}

const props = defineProps<UIInput>()
const emit = defineEmits(['update:modelValue'])

function emitChange(event: Event): void {
  if (event?.target?.value) {
    emit('update:modelValue', event.target.value)
  }
}

function errorDescriptionId(): string | undefined {
  if (props.error === undefined) {
    return undefined
  }

  return `${!!props.id ? props.id : generateRandomId()}-error`
}

function generateRandomId(): string {
  const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
  let random_string = ''

  for (let i = 0; i < 4; i++) {
    const index = Math.floor(Math.random() * letters.length)
    random_string += letters.charAt(index)
  }

  return random_string
}
</script>

父组件使用方式:

<UIInput
  autocomplete="off"
  id="complement"
  label="Complemento"
  type="text"
  v-model="form.address.complement"
/>
问题排查与解决

核心原因

Vue 2.7中使用Composition API的v-model时,存在Vue 2原生逻辑与Vue 3的差异:

  • Vue 2原生v-model默认绑定value prop和input事件,而非Vue 3的modelValue和update:modelValue。
  • 当前组件定义了modelValue prop,但Vue 2.7处理v-model时,会把父组件绑定值传入value prop,导致modelValue始终为undefined,值出现在$attrs.value中。

修复方案

有两种可行修复方式,按需选择:

方式一:适配Vue 2原生v-model规则

修改组件的prop和事件,贴合Vue 2原生逻辑:

  1. 将modelValue替换为value作为prop名称
  2. 触发input事件而非update:modelValue

修改后的组件脚本:

type UIInput = {
  // 其他prop保持不变
  value?: any // 替换modelValue为value
}

const props = defineProps<UIInput>()
const emit = defineEmits(['input']) // 替换为input事件

function emitChange(event: Event): void {
  const target = event.target as HTMLInputElement
  emit('input', target.value) // 触发input事件
}

同时模板中的:value="modelValue"需改为:value="value"。

方式二:启用Vue 3风格v-model(推荐)

Vue 2.7支持通过model选项开启Vue 3风格v-model,在组件中添加配置:

  1. 在<script setup>外添加普通脚本块,配置model选项
  2. 保持现有modelValue和update:modelValue逻辑不变

修改后的组件代码:

<template>
  <!-- 模板内容保持不变 -->
</template>

<script>
export default {
  model: {
    prop: 'modelValue',
    event: 'update:modelValue'
  }
}
</script>

<script lang="ts" setup>
// 原有setup脚本内容保持不变
type UIInput = {
  // 所有prop保持不变,包括modelValue
}
// 其他代码不变
</script>

配置后,Vue 2.7会将v-model绑定值映射到modelValue prop,同时监听update:modelValue事件更新父组件数据,和Vue 3逻辑完全一致。

额外优化点

  • emitChange函数中原条件判断会导致空值无法传递(如用户清空输入框),应直接传递目标值:
    function emitChange(event: Event): void {
      const target = event.target as HTMLInputElement
      emit('update:modelValue', target.value)
    }
    
  • 确保父组件中form.address.complement为响应式且初始化默认值:
    import { reactive } from 'vue'
    const form = reactive({
      address: {
        complement: ''
      }
    })
    

内容的提问来源于stack exchange,提问作者Bernardo Benini Fantin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 14:05:29