DEBUG.WIKI
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国
PanstarCloud:部署应用,接入 AI,云服务器与 AI 网关
OnlineSIM:在线接收短信,虚拟号码租用

Svelte 水合重复写入相同数字属性,观察器为何收到通知

SSR 属性正确,水合却再次触发同值 attribute 通知。Svelte 5.57.3 会跳过普通数字、布尔属性的冗余写入,property setter 仍需执行。

SSR 已输出正确的 tabindex、SVG 尺寸或 aria-rowindex,Svelte 水合时,MutationObserver 却再次收到这些属性的变化通知,旧值与当前值还完全相同。若属性来自数字或布尔表达式,应先区分多余写入与真实的服务端、客户端内容不一致。

Svelte 5.57.1 的普通属性路径会遇到这类重复写入。正式 5.57.3 已在水合期间跳过一部分序列化后相同的数字、布尔属性写入,同时保留 property setter 的执行。这不是要求水合阶段完全没有 DOM 变化。

字符串与数字相等的内容,不是相同的类型

服务端 HTML 中的属性通过 getAttribute() 读回来是字符串。例如 tabindex="-1" 对应字符串 '-1',客户端表达式 tabindex={-1} 对应数字 -1。旧路径严格比较这两个值,判断它们不同,接着又把 -1 写成同一个属性字符串。

一个用于说明条件的模板片段是:

<td tabindex={-1}>{label}</td>
<svg width={24} height={24} aria-hidden="true"></svg>

label 沿用页面已有值。只有页面先输出服务端 HTML、随后由客户端水合时,才涉及这里的初值比较;单纯客户端新建节点需要写入属性,不能拿来判断冗余水合写入是否消失。

已有 DOM 观察器若把任何 attribute 通知都当成业务变化,可能重复安排布局、同步或其他工作。应先检查通知的属性名、旧值与当前序列化值,而不是把通知条数直接等同于界面错误或固定的性能损失。不同页面的写入量与观察器行为并不相同。

更新普通属性路径,保留 setter 的作用

在兼容的构建环境中采用 Svelte 5.57.3,并确认编译与客户端运行解析到的是这份包。该实现只在仍走 setAttribute 的分支中,跳过水合时与服务端字符串相同的数字、布尔值。

如果属性有可用的 property setter,非字符串值仍可进入 setter。原生控件、画布尺寸和自定义元素可能依靠赋值产生实际作用;不能为了减少 observer 通知,把所有绑定值改成字符串,或者全局拦截 DOM 写入。布尔属性也有自己的存在性语义,disabled="false" 并不表示禁用状态为假。

观察器本身若只关心特定业务字段,可以保留其原有职责,仅在已确认不需要处理“序列化值未变”的路径上过滤重复工作。不要直接丢弃全部水合通知:服务端与客户端真实不匹配时,修正仍需被观察。

验收使用原 SSR 入口,在水合前建立观察,再分别检查普通数字属性、真正改变的属性,以及依赖 setter 的控件。预期是无意义的普通属性重写减少,真实变化与 setter 作用仍然存在;页面看起来相同或 observer 完全安静,都不能单独证明处理正确。