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修饰符,提升组件封装性:
然后在SingleGarage.vue中监听:<!-- 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><SingleCar :vehicle="vehicle" @car-click="testingTef(vehicleIndex)"></SingleCar>
内容的提问来源于stack exchange,提问作者Usama Khushnood
相关产品推荐
相关产品推荐

