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

Svelte递归组件<svelte:self>标签无法正常触发重渲染问题

Svelte 递归组件<svelte:self>更新不触发重渲染问题

问题表现

使用<svelte:self />实现类文件浏览器的递归渲染逻辑时,交互存在异常:首次点击任意标签渲染出对应子层级内容后,后续点击其他同层标签不会切换子内容,页面始终停留在首次渲染的子内容结果。
预期交互逻辑:点击general settings时展示其下的test、abc子标签,点击orders时切换展示ladida、haha子标签。
实际交互结果:首次渲染子内容后,始终展示test、abc标签,点击操作无法触发内容切换。

涉及代码

父组件 Parent.svelte

作为递归组件外层容器,负责定义组件尺寸、传入基础渲染数据:

<script>
    import RecursiveChild from 'RecursiveChild.svelte';

    const data: any = [
        {
            label: 'general settings',
            options: [
                { label: 'test' },
                { label: 'abc' },
            ],
        },
        {
            label: 'orders',
            options: [
                { label: 'ladida' },
                { label: 'haha' },
            ],
        },
    ];
</script>

<div class="main">
    <RecursiveChild paths={data} />
</div>

<style lang="scss">
    @import '../lib.scss';

    .main {
        @include fill();
        display: flex;
    }
</style>

递归子组件 RecursiveChild.svelte

使用<svelte:self />实现递归渲染,为异常发生组件:

<script>
    export let paths;

    let labels = paths ? paths.map((value) => value.label) : [];

    let pressedLabel: number;
    function handleClick(i: number) {
        pressedLabel = i;
    }
</script>

<div class="paths">
    {#each labels as label, i}
        <div class="label" on:click={() => handleClick(i)}>{label}</div>
    {/each}
</div>

{#if pressedLabel !== undefined}
    <svelte:self paths={paths[pressedLabel].options} />
{/if}

<style lang="scss">
    .paths {
        display: inline-flex;
        flex-direction: column;
    }

    .label {
        margin: 25px;
        background-color: green;
    }
</style>

故障原因

两个问题共同导致渲染异常:

  • labels为组件初始化时一次性计算的普通变量,未做响应式声明,当组件接收到的paths prop变更时,labels不会重新计算。
  • 递归渲染的<svelte:self>未绑定唯一key,选中项切换时Svelte会复用已存在的子组件实例,不会触发组件重新初始化,导致子组件保留旧状态、旧渲染结果。

修复方法

  • 将labels改为响应式声明,保证paths变更时自动重新计算标签列表。
  • 给<svelte:self>绑定当前选中索引作为key,选中项变化时强制子组件重新渲染,避免DOM复用导致的状态残留。

修复后RecursiveChild.svelte核心代码:

<script>
    export let paths;

    // 响应式计算标签列表
    $: labels = paths ? paths.map((value) => value.label) : [];

    let pressedLabel: number;
    function handleClick(i: number) {
        pressedLabel = i;
    }
</script>

<div class="paths">
    {#each labels as label, i}
        <div class="label" on:click={() => handleClick(i)}>{label}</div>
    {/each}
</div>

{#if pressedLabel !== undefined}
    <!-- 绑定key避免组件复用导致的状态残留 -->
    <svelte:self key={pressedLabel} paths={paths[pressedLabel].options} />
{/if}

<style lang="scss">
    .paths {
        display: inline-flex;
        flex-direction: column;
    }

    .label {
        margin: 25px;
        background-color: green;
        cursor: pointer;
    }
</style>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 16:18:21