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

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 → ... 无限循环。

解决思路

  1. 修正插槽方法传递方式:将直接执行的方法改为传递一个匿名函数,仅在需要调用时才执行:
<slot name="left" :is-selected="leftSelected" :toggleLeft="() => toggle('left')" />
<slot name="right" :is-selected="rightSelected" :toggleRight="() => toggle('right')" />

此时父组件插槽内可以直接使用toggleLeft作为点击事件:

<button @click="toggleLeft" ... />
  1. 保留当前父组件ref调用方式:如果继续使用父组件的toggle方法通过ref调用子组件方法,需要确保子组件插槽中没有直接执行toggle方法的代码(即去掉:toggleLeft="toggle('left')"这类写法),避免渲染时自动触发方法执行。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 07:35:29