为何在Svelte的each块中添加if语句会导致local过渡失效?
问题原因
当你在{#each}块内加入{#if}分支后,Svelte无法稳定追踪每个列表项对应的DOM元素身份——原本每个each项对应唯一的<div>,现在根据ID奇偶会渲染两个不同的<div>分支。当你删除或添加元素时,Svelte会判定是销毁旧分支元素+创建新分支元素,而非对原有元素执行卸载/挂载操作,这直接导致local过渡失效(local过渡仅在元素自身被挂载/卸载时触发,而非分支切换时)。
解决方案
方案1:将transition移到{#each}的直接子容器上
把条件分支放在带过渡的容器内部,保证每个each项对应唯一的根元素,让Svelte能稳定追踪元素身份:
{#if itemsFetched} {#each items as it (it.id)} <div transition:slide|local> {#if it.id%2 === 0} <span>render like this {it.id} {it.name}</span> {:else} <span>render this other way {it.id} {it.name}</span> {/if} <button on:click={() => removeItem(it)}>remove</button> </div> {/each} {/if}
这样既保留了条件渲染的逻辑,又让local过渡能正常触发——因为每个列表项的根<div>身份始终和each项绑定,增删时是对这个根元素执行卸载/挂载,符合local过渡的触发条件。
方案2:给分支元素绑定统一key(备选)
如果必须让条件分支的根元素不同,可以给每个分支添加和each项id绑定的key属性,强制Svelte识别它们是同一个元素的不同状态:
{#if itemsFetched} {#each items as it (it.id)} {#if it.id%2 === 0} <div transition:slide|local key={it.id}> <span>render like this {it.id} {it.name}</span> <button on:click={() => removeItem(it)}>remove</button> </div> {:else} <div transition:slide|local key={it.id}> <span>render this other way {it.id} {it.name}</span> <button on:click={() => removeItem(it)}>remove</button> </div> {/if} {/each} {/if}
不过方案1更简洁,是Svelte处理这类场景的常规做法。
补充说明
local过渡的核心是:仅当元素自身被挂载/卸载时触发,而非父元素的条件渲染导致的间接挂载/卸载。你最初的代码里,itemsFetched控制父级显示,local能避免初始批量渲染触发过渡,正是因为元素是在父级显示后一次性挂载,local忽略了这种“间接挂载”;而增删元素时是元素自身的挂载/卸载,所以触发过渡。- 加入
{#if}分支后,每个each项对应的DOM元素不再唯一,Svelte的身份追踪断裂,导致增删时的操作变成分支切换,而非元素自身的卸载/挂载,所以local过渡不触发。
内容的提问来源于stack exchange,提问作者425nesp
相关产品推荐
相关产品推荐

