Vue 2父子组件插槽引发无限循环问题排查求助
父组件调用子组件方法引发无限循环的问题分析
问题背景
父组件通过ref调用子组件toggle方法时触发无限循环,尝试过插槽传递方法等方案均未解决,需明确问题根源。
组件代码
子组件(ToggleButtons.vue)
<template> <div role="list"> <div role="listitem"> <slot name="left" :is-selected="leftSelected" :toggleLeft="toggle('left')" /> </div> <div role="listitem"> <slot name="right" :is-selected="rightSelected" :toggleRight="toggle('right')" /> </div> </div> </template> <script> export default { props: { leftSelectedInitially: { type: Boolean, default: true, } }, data() { return { leftSelected: true, rightSelected: false, } }, beforeMount() { this.leftSelected = this.leftSelectedInitially; this.rightSelected = !this.leftSelectedInitially; }, methods: { toggle(override) { this.leftSelected = override == 'left'; this.rightSelected = override == 'right'; } } } </script>
父组件中使用ToggleButtons
<ToggleButtons ref="tb"> <template v-slot:left="{ isSelected }"> <button class="button" :class="{ secondary: !isSelected }" :aria-pressed="isSelected" :togglable="true" v-text="'left'" @click="toggle('left')" /> </template> <template v-slot:right="{ isSelected }"> <button class="button" :class="{ secondary: !isSelected }" :aria-pressed="isSelected" :togglable="true" v-text="'right'" @click="toggle('right')" /> </template> </ToggleButtons>
父组件的toggle方法
toggle(direction) { this.$refs.tb.toggle(direction); },
问题根源
无限循环的核心原因在子组件的插槽属性绑定上:
<slot name="left" :is-selected="leftSelected" :toggleLeft="toggle('left')" />
这里的toggle('left')是直接执行方法,而非传递方法引用。Vue在组件每次渲染时,都会执行这个方法,而toggle方法会修改leftSelected和rightSelected的数据——数据变更会触发组件重新渲染,重新渲染时又会再次执行toggle('left'),如此往复形成无限循环。
简单来说:渲染 → 执行toggle修改数据 → 数据变更触发重新渲染 → 再次执行toggle → ... 无限循环。
解决思路
- 修正插槽方法传递方式:将直接执行的方法改为传递一个匿名函数,仅在需要调用时才执行:
<slot name="left" :is-selected="leftSelected" :toggleLeft="() => toggle('left')" /> <slot name="right" :is-selected="rightSelected" :toggleRight="() => toggle('right')" />
此时父组件插槽内可以直接使用toggleLeft作为点击事件:
<button @click="toggleLeft" ... />
- 保留当前父组件ref调用方式:如果继续使用父组件的
toggle方法通过ref调用子组件方法,需要确保子组件插槽中没有直接执行toggle方法的代码(即去掉:toggleLeft="toggle('left')"这类写法),避免渲染时自动触发方法执行。
内容的提问来源于stack exchange,提问作者bearaxe
相关产品推荐
相关产品推荐

