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

Svelte 5中传递响应式Props给子组件时响应性丢失问题

Svelte 5响应式丢失问题:传递类实例属性给子组件后UI不更新

问题背景

我在理解Svelte 5响应式机制时遇到了问题,具体情况如下:

我封装了一个DataResponse类来处理数据请求,内部属性用$state声明以实现响应式:

class DataResponse {
  data = $state([]);
  error = $state();
  isLoading = $state(false);
}

同时编写了useFetchData工具函数,用于发起请求并返回该类的实例,请求返回后会重新赋值data属性:

export function useFetchData() {
  const resp = new DataResponse();

  async function fetchData() {
    resp.isLoading = true;
    try {
      const response = await fetch("https://randomuser.me/api/?results=5");
      const j = await response.json();

      resp.data = j.results;
      resp.error = undefined;
    } catch (err) {
      resp.error = err;
      resp.data = undefined;
      console.error(err);
    } finally {
      resp.isLoading = false;
    }
  }

  fetchData();
  return resp;
}

正常工作的场景

当在调用处直接使用这些数据时,响应式符合预期,API请求返回后UI会自动更新:

<script>
    import { useFetchData } from "./useFetchData.svelte.js";

    const resp = useFetchData();  
</script>

{#each resp.data as person}
    <div>{person.name.first} {person.name.last}</div>
{/each}

响应式丢失的场景

但当我把UI逻辑提取到独立的子组件中,并尝试将resp.data作为props传递给子组件时,响应性丢失,UI始终不会更新:

父组件代码:

<script>
    import { useFetchData } from "./useFetchData.svelte.js";
    import People from "./People.svelte";

    const resp = useFetchData();  
</script>

<People people={resp.data} />

子组件People.svelte代码:

<script>
    let people = $props();
</script>

<h1>People</h1>
{#each people as person}
    <div>{person.name.first} {person.name.last}</div>
{/each}

问题

请问我哪里出错了?


解答

原因分析

Svelte 5的响应式追踪基于值的引用变化和**$state的访问时机**。当你把resp.data作为props传递给子组件时,初始传递的是空数组的引用。当请求完成后resp.data被重新赋值为新数组时,父组件并没有感知到这个变化——因为父组件只在初始化时读取了一次resp.data,后续没有任何响应式上下文(比如模板、$derived)追踪这个值的更新,所以不会重新渲染并传递新的props给子组件。

解决方案

有两种可靠的修复方式:

方案1:传递整个resp实例给子组件

由于DataResponse类内部的属性是用$state声明的,整个实例本身具备响应式能力。传递实例给子组件后,子组件访问response.data时会触发响应式追踪,自动感知变化:

父组件修改:

<script>
    import { useFetchData } from "./useFetchData.svelte.js";
    import People from "./People.svelte";

    const resp = useFetchData();  
</script>

<People response={resp} />

子组件People.svelte修改:

<script>
    let response = $props();
</script>

<h1>People</h1>
{#each response.data as person}
    <div>{person.name.first} {person.name.last}</div>
{/each}

方案2:在父组件中让resp.data进入响应式上下文

可以通过$derived创建派生值,或者在父组件模板中添加对resp.data的引用,让父组件的响应式系统感知到resp.data的变化:

方式一:使用$derived包装

<script>
    import { useFetchData } from "./useFetchData.svelte.js";
    import People from "./People.svelte";
    import { $derived } from 'svelte';

    const resp = useFetchData();
    const people = $derived(resp.data);
</script>

<People people={people} />

方式二:在父组件模板中添加追踪标记

<script>
    import { useFetchData } from "./useFetchData.svelte.js";
    import People from "./People.svelte";

    const resp = useFetchData();  
</script>

<!-- 空的条件判断用于触发对resp.data的响应式追踪 -->
{#if resp.data}{/if}
<People people={resp.data} />

原理补充

Svelte 5采用惰性响应式追踪,只有当值在响应式上下文(组件模板、$derived、$effect等)中被访问时,才会建立依赖关系。最初的错误代码中,父组件没有持续追踪resp.data的变化,因此无法感知到其更新,也就不会触发子组件的props更新。


内容的提问来源于stack exchange,提问作者zmb

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 22:49:55