DEBUG.WIKI
晚安云;香港 CN2 ¥15/月
PanstarCloud:部署应用,接入 AI,云服务器与 AI 网关
野草云;香港 200Mbps 峰值;不限月流量 · BGP VPS;查看套餐 →

Svelte 5.57.2 派生值漏掉新增属性:检查 derived 是否在 effect 外创建

模板内 hasOwn 已能更新,事件中创建的类派生值仍卡住,可能是缺失键没有响应式 source。读取属性后再做存在性判断可保留语义。

点击按钮给 $state 新增字段后,直接读取字段的派生值更新了,'x' in state 或 Object.hasOwn(state, 'x') 却仍显示 false。若派生值来自事件处理器里新建的类,检查它创建时是否没有父 effect。

这条条件在 Svelte 5.57.2 中仍存在:缺失属性第一次只经过 has 查询,而 derived 创建于 effect 之外。模板或已有 effect 先读取过同一个键时,可能已经建立响应式 source,因此相似页面又看不出问题。

5.57.1 的 hasOwn 修复没有覆盖所有创建位置

此前自有属性描述查询缺少追踪的路径,已经在 5.57.1 修正。但 5.57.2 的 has trap 为不存在的键新建 source 时,仍受 active_effect !== null 条件限制。无父 effect 的 derived 执行时不满足它,便没有可跟踪的键。

直接 get 读取有另一条创建路径,因此 state.x 仍可能正常。这不表示赋值失败,也不表示 Object.hasOwn 与 in 在业务语义上可以随意互换。

需要存在性语义时,先明确读取同一个键

可在受影响的派生计算中先读取属性,再保留原存在性检查。下面是类字段的局部示例,适用于支持 runes 的 Svelte 编译文件:

class Store {
  state = $state({});
  hasX = $derived.by(() => {
    void this.state.x;
    return Object.hasOwn(this.state, 'x');
  });
}

void 丢弃读取结果,但这次 get 会给缺失键建立追踪依据;最终布尔值仍由 Object.hasOwn 决定。类需沿原来的事件或模块创建位置使用,不能把它挪到模板里后就宣称原条件已恢复。

这项临时处理面向普通状态数据。属性若是 getter、对象带有其他自定义代理或读取本身有副作用,额外 get 会改变行为,应先核对。动态键也必须在同一次计算中读取和检查相同名称。

不能直接换成 state.x !== undefined:拥有 x 且值为 undefined,与根本没有 x 是不同状态。in 还包含原型链,也不能代替要求自有字段的判断。

新增、undefined 与删除都要走过

在原来无父 effect 的创建入口验证:初始没有 x 时为 false;新增 x: true 后为 true;保留 x 但写 undefined 时仍为 true;删除 x 后回到 false。再覆盖同键的直接读取,确认没有为了让页面更新而改掉存在性规则。

若后续采用正式框架修复,应保留这组创建位置和状态变化做回归,再决定是否移除额外读取。已核对的 5.57.2 正式代理源码仍含父 effect 条件,不能因为较早的 hasOwn 修复已发布就忽略此分支。回退时保存原版本和局部计算,普通快照或非代理对象仍不会自动获得响应式能力。