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

Nuxt3 Composition API中如何正确使用emit与props实现组件传值

问题排查

当前代码共有3个核心错误,直接导致父子组件数据无法同步:

  • 违反Vue单向数据流规则:props是父组件向子组件传递的只读数据,子组件不能直接修改props值,你在子组件中直接给v-model绑定AirportData这个prop,属于非法修改,哪怕你在子组件里看到值变化,也不会同步回父组件,开发环境下Vue会直接抛出警告。
  • 父组件事件回调存在变量名冲突:handleUpdate函数的形参名和外层定义的ref变量pickuppoint完全同名,函数执行时会优先读取内部形参,你写的pickuppoint.value = pickuppoint本质是给形参对象加value属性,根本修改不到外层的ref变量。
  • 子组件取值逻辑冗余:@item-select事件本身会把选中的机场选项对象作为参数返回,不需要绕一圈读取props里的值,直接拿事件参数传递更准确,也避免props不同步的问题。
正确实现方案

修正后的子组件 components/AirportSearch.vue

子组件内部单独维护选中状态,不直接修改props,选中选项后直接把值抛给父组件,同时支持父组件传入初始值做历史数据回显:

<template>
  <AutoComplete
    class="ttc-w-full ttc-h-12"
    v-model="selectedAirport"
    :suggestions="airports"
    @complete="getairports($event)"
    field="name"
    placeholder="Search Pickup Location"
    forceSelection
    @item-select="handleSelect"
  >
    <template #item="slotProps">
      <div
        style="display: flex; justify-content: space-between"
        class="ttc-font-bold ttc-text-md"
      >
        {{ slotProps.item.name }}
        <span class="ttc-font-normal ttc-text-xs">
          {{ slotProps.item.code }}
        </span>
      </div>
    </template>
  </AutoComplete>
</template>

<script setup>
import { ref, watch } from "vue";
const { apiUrl } = useRuntimeConfig();
const airports = ref([]);
const emit = defineEmits(["update:pickupPoint"])

// 接收父组件传入的默认回显值
const props = defineProps({
  defaultAirport: {
    type: Object,
    default: null
  }
});

// 子组件内部维护选中状态,不直接修改props
const selectedAirport = ref(props.defaultAirport);

// 监听父组件传入的默认值变化,处理cookie异步加载后的回显
watch(() => props.defaultAirport, (newVal) => {
  if (newVal) selectedAirport.value = newVal;
})

const getairports = useDebounce(async (event) => {
  const { data } = await useFetch(`${apiUrl}/airports/find`, {
    method: "post",
    headers: {
      Accept: "application/json",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      aptsearchkey: event.query,
    }),
  });
  airports.value = data.value;
}, 500);

// 选中选项时直接把事件返回的选中值抛给父组件
function handleSelect(event) {
  emit("update:pickupPoint", event.value);
}
</script>

修正后的父组件 components/TaxiSearchForm.vue

修复变量名冲突问题,接收子组件传递的值,同时支持选中值自动存cookie:

<template>
 <div>
   <AirportSearch 
      :defaultAirport="pickuppoint"
      @update:pickupPoint="handleUpdate"
   />
   <!-- 调试用,可删除 -->
   <div v-if="pickuppoint" class="mt-2">已选机场:{{ pickuppoint.name }} ({{ pickuppoint.code }})</div>
  </div>
</template>
<script setup>
import {ref, watch} from 'vue'
        
const airportTransferData = useCookie("airportTransferData", {
  maxAge: 60 * 60 * 24 * 7,
});
        
const pickuppoint = ref(null);
        
// 形参改名,避免和外层ref变量重名
function handleUpdate(selectedVal) {
  pickuppoint.value = selectedVal;
};

// 选中值变化时自动同步到cookie
watch(pickuppoint, (newVal) => {
  if (newVal) {
    airportTransferData.value = {
      ...airportTransferData.value,
      pickuppoint: newVal
    }
  }
}, { deep: true })
    
onMounted(() => {
  if (airportTransferData.value?.pickuppoint) {
    pickuppoint.value = airportTransferData.value.pickuppoint;
  }
});
</script>
优化建议

如果想写得更简洁,可以把子组件的emit名改成update:modelValue,父组件直接用v-model="pickuppoint"绑定,不需要手动写事件监听,是Vue3官方推荐的组件双向绑定写法。
你提供的机场接口返回数据结构是标准对象,不需要额外拆字段,直接传整个对象即可,后续存cookie、提交表单都可以直接用。

内容的提问来源于stack exchange,提问作者imgrv

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 02:51:21