Svelte hasOwn 没有更新
Svelte 状态新增属性后,Object.hasOwn 查询仍显示旧值,检查 5.57.0 的代理追踪缺口与 5.57.1 修复,并保留自有属性判断语义。
Svelte 5 中,给 $state({}) 新增属性后,使用 in 的页面会更新,Object.hasOwn() 的页面却仍显示旧值。这可能是旧状态代理没有在自有属性查询时建立响应式依赖,并不一定是赋值没有发生。
该表现可在 5.57.0 的组件中观察到。正式版 5.57.1 修正了对应代理处理,5.57.2 也包含这项修复。这里针对状态代理上的属性查询,普通对象本来不会自动变成响应式数据。
把值变化与查询变化分开看
下面是用于说明条件的组件:
<script>
let record = $state({});
</script>
<button onclick={() => record.name = 'Ada'}>添加 name</button>
<p>{Object.hasOwn(record, 'name')}</p>
初始没有 name,点击后对象中明确写入了这个属性。如果已有日志或调试信息能够确认属性已经出现,而模板仍保留 false,才需要检查属性存在性查询有没有被追踪。
查询必须发生在模板、派生值或其他有效的响应式读取上下文中。把查询结果提前存到一个普通局部常量,再期待这个常量随对象变化更新,是另一种数据流问题;升级代理实现不会让一次性的普通 JavaScript 计算自动重复执行。
自有属性与 in 的含义不同
Object.hasOwn(record, 'name') 检查对象自身是否有这个属性。'name' in record 还会考虑原型链。
因此,把全部 Object.hasOwn 替换成 in,可能让页面恢复变化,却同时改变判断条件。业务若需要区分对象自身提供的字段与继承字段,就应保留自有属性语义,选择包含修复的兼容版本。
同样,record.name !== undefined 不等于属性存在性:对象可能明确拥有一个值为 undefined 的属性。用这个表达式绕过旧问题,会把“没有字段”和“字段值为 undefined”合在一起。
修复的是代理的依赖追踪
Object.hasOwn 会触及对象的自有属性描述查询。旧代理在这条路径上没有建立新增属性所需的追踪关系,页面第一次查询不存在的属性之后,就可能错过后续新增。
已核对的正式实现让 getOwnPropertyDescriptor 先进入相应的属性存在性追踪,再返回自有属性描述。这样既保留 Object.hasOwn 的结果含义,也让响应式上下文知道该属性随后发生了变化。
项目可以沿框架与插件支持的兼容路径升级,再重新生成客户端产物。需要确认实际编译和运行使用的 Svelte 版本;只更新清单、浏览器却仍加载旧资源,不会改变代理逻辑。
如果数据经过 $state.snapshot、JSON 转换或其他库变成了普通对象,要单独检查代理边界。修复状态代理,并不会让已经离开代理的数据重新获得追踪能力。
同时检查新增与删除
在受控环境中,让模板查询一个起初不存在的自有属性,再新增并删除它,确认页面依次显示预期结果。属性存在且值为 undefined 的情况也应保留正确判断。
还要检查现有的 in 查询仍能正常更新,并保留自有属性与继承属性的差别。页面显示不再卡住,只是验收的一部分;为了更新而悄悄换掉业务判断语义,会留下另一处错误。


