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

