Svelte 水合让动画重播
SSR 动态 svelte:element 水合后动画又从头播放,检查认领节点被多余移动。核对 Svelte 5.57.2 与正常标签变化的区别。
首次打开 SSR 页面,<svelte:element> 内的 CSS 动画先播放一次,客户端水合到达后又从头播放;相同位置的静态 <div> 却正常。这个差异值得先检查动态元素是否被移除、重新插入,而不只是继续调整动画时长。
具体问题在 Svelte 5.55.2 被观察到,5.57.1 中对应插入路径仍未修正。已核对的正式 5.57.2 保留水合时认领的动态节点位置,同时保留客户端新建元素与后续标签变化的插入行为。
同一个节点,也可能重新触发生命周期
SSR 已经输出元素,水合应认领并复用它。旧 <svelte:element> 路径认领节点后,仍执行 anchor.before(element)。节点对象没有换成新的,重新移动却可以触发移除与插入的影响,使 CSS 动画重启、custom element 断开再连接。
首次绘制与水合靠得很近时,第二次播放容易被忽略。客户端代码加载较慢时,两次播放之间的间隔更明显。因此,“本机热加载看不到”不足以排除首次导航的问题。
检查时固定动态标签值,对比同样样式的静态元素。可以在受控测试中观察父节点的 childList 变化、动画开始时间,以及 custom element 的连接回调;先确认额外移动与初始水合时间相符。不要只看元素对象是否相等:对象相同仍可能被移位。
下面是两种元素写法的对照片段,应放进已有的 SSR 页面,tag 由该页面提供,.box 沿用需要检查的动画样式:
<div class="box">静态元素</div>
<svelte:element this={tag} class="box">
动态元素
</svelte:element>
它没有启动服务器、注入延迟或自动证明故障。实际测试还需确认页面确实先有服务端 HTML,再由客户端水合,而不是单纯客户端挂载。
水合时留在原位,标签变化时正常替换
5.57.2 在处理子内容前保存这次操作的水合状态,之后只为非水合路径插入元素。这个顺序很重要:空元素或 void 元素处理子内容时,可能临时改变水合标志;等到最后再读全局标志,会把已认领节点误当成客户端新建节点。
选择包含此修复的兼容正式版本后,核对实际解析的 Svelte 包,并重新测试冷启动页面。无需为此关闭整个页面的 SSR,也不应全局禁用动画或手工搬动 Svelte 管理的 DOM。
若业务真的改变了 this={tag} 的标签,替换元素本来就是预期行为;标签变成空值时,元素也应消失。服务端与客户端输出不一致的水合错误,则要另查两边的数据与标签是否一致。这里的修复不能让任意不匹配都保持原节点。
验收应看到:初始水合没有额外移除、重插已经认领的动态元素,CSS 动画不因这次多余移动重新开始;真正的标签变化与客户端首次挂载仍正常。页面里包含 iframe 或 custom element 时,进一步确认它们的重载、连接次数符合业务预期,不能只凭动画看起来平滑就认为所有生命周期影响都已消失。


