Svelte 给 $state.toString 赋值后字段消失:先检查原型同名键
Svelte 5.57.2 的状态代理可能丢弃后来添加的原型同名字段,SSR 与客户端也会分歧。初始化为自有字段可作局部处理,但不等于修复任意动态键。
向 $state({}) 写入 toString 后,Object.hasOwn 仍为 false,JSON 里也没有该字段。若普通字段能正常保存,检查名字是否与对象原型成员相同。
Svelte 5.57.2 的这一条件不只是页面没更新。给起初为空的代理赋值 state.toString = 'own',值可能根本没有进入状态;读取仍是继承来的函数,快照、展开与序列化也看不到新字段。valueOf、constructor 等原型同名键也需按同样条件排查。
先确认是值没写入,还是查询没追踪
在已有诊断中,对照同一时刻的直接读取、自有属性判断及序列化结果。若值已经在对象里,只有响应式页面停留在旧布尔值,应另查属性查询的依赖追踪;本题的关键是后来添加的同名键被丢弃。
5.57.2 的 set trap 用 prop in target 判断键是否已存在。继承键会让结果为 true,但目标上又没有该键的自有可写描述,创建响应式 source 的分支因此被跳过。这个差别解释了为何普通新字段能写,而已有原型名字不行。
已知字段可在创建状态时明确提供
如果业务确实有名为 toString 的数据字段,且字段集合已知,可以在状态初始化时就把它定义为自有字段:
<script>
let fields = $state({ toString: '' });
</script>
<input bind:value={fields.toString} />
<p>{JSON.stringify(fields)}</p>
这样后续更新面对的是已经存在的自有可写字段,而不是后来才要遮蔽的继承成员。保留真实字段名和服务端协议;若字段是可选的,预置空字符串会改变“字段缺失”的语义,必须先确认提交和校验约定允许。不能把所有可选字段都悄悄补为空值。
拥有字符串 toString 的普通对象本来也会改变字符串转换行为,所以不要再依赖 String(fields) 调用默认方法。这里处理的是业务字段,不是给对象实现新的转换函数。
如果表单库负责在内部创建状态,单独在组件中初始化另一个对象不会修复库内部的写入。应在库支持的初值入口提供字段,或等待并采用经核对的正式修正。对于任意动态外部键,设计明确的数据容器和输入边界,不能靠预列几个名字宣称全部解决。
同时检查 SSR、客户端和提交值
SSR 的 $state 不经过同样的客户端代理,初始化期间的写入可能在服务端保留、水合后丢失。检查服务端 HTML、客户端交互及真正提交给后端的值,尤其注意页面看似能输入但提交仍为空的情况。
覆盖初值、修改、序列化和原有可选字段逻辑。不要用 delete state.toString 清理现场:该版本删除继承键还有另一条行为差异,可能让原型成员也无法正常读取。当前正式 5.57.2 仍保留相关 set 判断;较早的 hasOwn 追踪修正不代表这个赋值缺口也已消失。


