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

Vue.js子组件向父组件Emit事件失效问题排查求助

问题排查:Vue子组件Emit事件无响应及重复渲染问题

我来帮你一步步排查这两个问题,先解决当前最棘手的Emit无响应问题,再分析之前的重复渲染原因:

一、子组件Emit事件无响应的核心问题及修复步骤

你的代码里有两个关键错误导致Emit事件没被正确捕获:

1. 事件监听的位置完全错误

你把@emitData监听放在了<b-sidebar>组件上,但实际上emitData是由<SingleGarage>子组件向上发送的事件,应该在父组件的<SingleGarage>标签上监听这个事件——侧边栏根本不会触发这个事件,自然无响应。

2. 事件监听的写法有误

即使监听位置正确,@emitData="testingEmit()"这种写法会在页面初始化时直接执行testingEmit函数,而不是在事件触发时执行,而且无法接收子组件传递的参数。

修复后的关键代码调整

Home.vue(父组件)

把事件监听移到<SingleGarage>标签上,同时修正监听写法;另外把侧边栏的显示逻辑移到testingEmit方法里,让逻辑更集中:

<template>
  <div class="home">
    <!-- warehouse details -->
    <div v-for="(detail, detailindex) in details" :key="detailindex" class="container mt-5 mb-5" >
      <h1> {{ detail.name }} <span class="location">{{ detail.cars.location }}</span> </h1>
      <!-- 在这里监听子组件的emit事件 -->
      <SingleGarage :detail="detail" @emitData="testingEmit"></SingleGarage>
    </div>
    <b-sidebar id="my-sidebar" title="Sidebar with backdrop" backdrop-variant="dark" ref="mySidebar" backdrop shadow>
      <div class="px-3 py-2">
        <h1>{{currentCar}}</h1>
      </div>
    </b-sidebar>
  </div>
</template>

<script>
// @ is an alias to /src
import axios from "axios";
import SingleGarage from "../components/SingleGarage";
export default {
  components: { SingleGarage },
  name: "Home",
  data: () => ({
    details: [], // 修正:接口返回数组,初始化为数组更合理
    currentCar: '', // 修正:初始化为空字符串避免显示异常
  }),
  methods:{
    testingEmit(data){
      this.currentCar = data
      console.log('data from emit',data)
      // 触发侧边栏显示
      this.$root.$emit('bv::toggle::collapse', 'my-sidebar')
    }
  },
  mounted() {
    // 原有的axios请求代码不变
    axios
    .get("https://api.jsonbin.io/b/5ebe673947a2266b1478d892")
    .then((response) => {
      var results;
      response.data.forEach((element) => {
        element.cars.vehicles.sort((a, b) => {
          a = new Date(a.date_added);
          b = new Date(b.date_added);
          results = a > b ? -1 : a < b ? 1 : 0;
          return results * -1;
        });
      });
      this.details = response.data;
    });
  },
};
</script>

SingleGarage.vue(子组件)

不需要再用this.$parent.$emit,直接用this.$emit向父组件发送事件即可——$parent的使用容易出错(比如组件嵌套层级变化时),父子组件通信优先用标准的emit:

<script>
import SingleCar from "@/components/SingleCar";
export default {
  name: "SingleGarage",
  components: { SingleCar },
  props: ["detail"],
  data: () => ({
    dummyImg: require("@/assets/img/dummycar.png"),
    currentCar : 1
  }),
  methods: {
    testingTef(vehicleIndex) {
      // 直接向父组件emit事件
      this.$emit('emitData', this.detail.cars.vehicles[vehicleIndex].make)
      console.log(this.detail.cars.vehicles[vehicleIndex].make)
      console.log(this.detail.cars.vehicles[vehicleIndex].date_added)
      this.currentCar = this.detail.cars.vehicles[vehicleIndex].make;
    },
  },
};
</script>

二、之前侧边栏重复渲染四次的可能原因(供参考)

你提到之前有侧边栏重复渲染四次的问题,结合你的代码来看,大概率是因为:

  • 你之前可能误将<b-sidebar>放在了<div v-for="(detail, detailindex) in details">循环内部,导致每条detail数据都渲染一次侧边栏,而接口返回了4条数据,所以出现四次重复渲染。
  • 另外,v-for使用detailindex作为key虽然能工作,但如果数组元素有唯一标识(比如detail的id),建议用唯一值作为key,避免因数组顺序变化导致的不必要渲染。

额外优化建议

  • 避免使用$parent或$root来发送事件,尽量通过父子组件的props和emit进行通信,这样代码更清晰,也便于维护。
  • 可以把<SingleCar>的点击事件封装到组件内部,不用native修饰符,提升组件封装性:
    <!-- SingleCar.vue -->
    <template>
      <div class="singleCar" @click="handleCarClick">
        <!-- 原有内容不变 -->
      </div>
    </template>
    <script>
    export default {
      name: "SingleCar",
      props: ["vehicle"],
      data: () => ({
        dummyImg: require("@/assets/img/dummycar.png"),
      }),
      methods:{
        handleCarClick(){
          this.$emit('car-click')
        }
      }
    };
    </script>
    
    然后在SingleGarage.vue中监听:
    <SingleCar :vehicle="vehicle" @car-click="testingTef(vehicleIndex)"></SingleCar>
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 06:49:09