DEBUG.WIKI
京东云;让 AI 进入真实业务;零售 · 物流 · 客服 · 硬件;查看方案 →
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国
Evoxt;最高 6.0GHz CPU;高频云服务器 · 免费每周备份;查看配置 →

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 配置函数,仍需检查该函数返回值;升级不会替应用补齐无效配置。