Svelte crossfade 报 reset 错误
out-only crossfade 分支被 effect 在同一轮更新恢复,旧 Svelte 可能访问未初始化动画。核对 5.57.2 延迟接口修复与动画位置。
带 out:send 的条件分支刚被移除,又在同一轮更新中被 $effect 恢复,Svelte 却抛出:
Cannot read properties of undefined (reading 'reset')
堆栈包含 Object.reset、Object.in 与 resume_children 时,检查是否使用了 crossfade 这类延迟 transition。Svelte 5.57.0 有这个具体竞态;已核对的正式版 5.57.2 修正了内部动画尚未初始化时的调用。
分支恢复早于动画初始化
延迟 transition 的函数先返回另一个函数,要等 DOM 更新后才计算动画配置。运行时先交出一层同步接口,内部真正的 animation 会在稍后的微任务中创建。
outro 开始后,如果 effect 又把同一分支恢复,运行时需要中断退出并恢复子元素。旧接口直接调用内部 animation 的 reset,但那时它可能还不存在,于是出现 undefined 报错。
诊断要保留这几个条件:只有退出 transition、使用延迟配置、条件分支在同一轮更新里移除再恢复。下面是能体现这份顺序的完整组件示例,供受控测试环境核对,不是要求业务页面必须采用这种状态组织:
<script>
import { crossfade } from 'svelte/transition';
let visible = $state(true);
let restore = $state(false);
const [send] = crossfade({});
$effect(() => {
if (restore) visible = true;
});
</script>
<button onclick={() => {
visible = false;
restore = true;
}}>恢复分支</button>
{#if visible}
<p out:send={{ key: 'item' }}>内容</p>
{/if}
这里 $effect 内的赋值正是触发顺序的一部分。删掉它后没有报错,只能说明没有走到原来的恢复路径,不能证明延迟 transition 已经安全。
核对正式版本与动画位置
5.57.2 的延迟接口在内部 animation 尚未创建时,安全处理 reset、deactivate 与 abort,并提供初始化前的动画位置。修复不只是给某一处 undefined 加判断,还要让后续过渡从正确位置接续。
优先使用与项目兼容、包含该修复的正式版本。不要只凭 PR 已合并推断锁文件安装的旧包已有改动。
在旧环境中随意加入 setTimeout 或 tick,可能把恢复挪到另一轮更新,暂时避开竞态,也改变了用户看到的状态与动画时序。只有业务本身允许延期时才考虑这种重排,不能把固定等待时间当成通用修复。删除所有 transition 同样会损失原本需要的交互反馈。
验收时先核对同一轮移除、恢复不再抛 reset 异常,再看分支是否保持预期可见、动画是否从正确位置继续。正常退出、快速反向切换与普通进入动画也应保留。若错误来自自己的 transition 配置函数,仍需检查该函数返回值;升级不会替应用补齐无效配置。


