You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Svelte中$state([])内嵌套对象无响应的修复方法咨询

Svelte中$state([])内嵌套对象无响应的修复方法咨询

我最近在把Svelte 4的地图相关组件迁移到Svelte 5,碰到了一个 reactivity 相关的问题,折腾了好久没搞定,想请教下大家怎么解决。

具体情况是这样的:
我用$state([])声明了一个响应式数组waypointsBearings,数组里的每个元素都是包含enabled、angle、degrees属性的对象。当我点击按钮添加waypoint(用模拟的addWaypoint函数替代真实地图的添加事件),然后切换复选框来修改某个waypoint的enabled属性时,Svelte完全没检测到这个变化——我写在$effect里的日志根本不输出,换成$inspect也看不到任何更新。

先给大家看关键的代码片段,还有模拟的场景说明:

模拟场景说明

下面的addWaypoint是模拟函数,用来替代真实地图的事件监听逻辑(原本的地图事件监听代码被注释掉了),目的是复现waypoint添加的交互:

组件脚本部分

let { directions, configuration } = {directions: {waypointsBearings: [], configuration: {bearings: true}}, on: () => {}, configuration: {}}

// @ts-expect-error It's safe to read the plugin's protected properties here.
if (!directions.configuration.bearings) {
  console.warn("The Bearings Control is used, but the `bearings` configuration option is not enabled!");
}

// 模拟添加waypoint的函数,替代真实地图的事件监听
function addWaypoint() {
    directions.waypointsBearings.push({enabled: false})
    onWaypointsChanged()
}

// 用Svelte 5的$state声明响应式数组
let waypointsBearings: {
    enabled: boolean;
    angle: number;
    degrees: number;
}[] = $state([]);

// 同步directions的waypoints到本地的waypointsBearings数组
function onWaypointsChanged() {
    waypointsBearings = directions.waypointsBearings.map((waypointBearing, index) => {
      if (waypointsBearings[index]) return waypointsBearings[index];

      return {
        enabled: configuration.defaultEnabled || !!waypointBearing,
        angle: waypointBearing ? waypointBearing[0] : configuration.angleDefault,
        degrees: waypointBearing
          ? waypointBearing[1]
          : configuration.fixedDegrees
            ? configuration.fixedDegrees
            : configuration.degreesDefault,
      };
    });
}

// 这个effect不会响应嵌套对象的enabled属性变化
$effect(() => {
  console.log("PIA");
  console.log(waypointsBearings);
});

onWaypointsChanged();

组件模板部分

<button onclick={addWaypoint}>Add</button>
<div class="bearings-control {configuration.class}" style="display: {waypointsBearings.length ? 'block' : 'none'}">
  <div class="bearings-control__list">
    {#each waypointsBearings as waypointBearing, i}
      <div
        class="
        bearings-control__list-item
        {waypointBearing.enabled ? 'bearings-control__list-item--enabled' : 'bearings-control__list-item--disabled'}
        "
      >
        <span class="bearings-control__number">{i + 1}. </span>
        <input type="checkbox" bind:checked={waypointBearing.enabled} class="bearings-control__checkbox" />
      </div>
    {/each}
  </div>
</div>

补充背景

原来在Svelte 4中的对应组件是完全正常工作的:点击地图添加waypoint后,对应的bearings控制栏会正常出现,切换复选框修改enabled属性时,组件能正确响应并更新状态,但迁移到Svelte 5使用$state声明数组后,就出现了这个响应丢失的问题。

我猜测是不是Svelte 5的 reactivity 追踪机制和Svelte 4不一样?比如$state声明的数组,只能检测到数组本身的引用变化(比如重新赋值、push/pop等操作),但无法追踪数组内嵌套对象的属性修改?

有没有大佬知道怎么解决这个问题?比如有没有办法让Svelte 5检测到数组内嵌套对象的属性变化,或者有没有其他更适配Svelte 5的写法可以替代当前的实现?

备注:内容来源于stack exchange,提问作者smellyshovel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 13:19:32