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

API返回数据后如何正确重渲染Svelte子组件?

问题描述

我有一个会进行API调用的ParentComponent,希望在API调用完成后,依赖返回的items数组的ChildComponent能自动用新数据重新渲染。

我见过一种强制组件重渲染的技巧:

<script>
  if(rerenderComponent) rendered = true;
</script>

{#key rendered}
  <ChildComponent {items} />
{/key}

但这感觉是逼着Svelte做它本不需要做的事,想知道有没有更符合Svelte风格的正确做法?

以下是我的代码:

App.svelte

<script>
  import ParentComponent from './testing/ParentComponent.svelte'
</script>

<ParentComponent />

ParentComponent.svelte

<script>
  import { onMount } from 'svelte'
  import ChildComponent from './ChildComponent.svelte'

  let items = []
  let value = ''

  onMount(async function () {
    console.log('ParentComponent onMount')
    await loadItems()
  })

  async function loadItems() {
    console.log('loadItems started:')
    const response = await getItemsFromApi()
    const data = await response
    items = data['items']
    console.log('loadItems completed:', { items })
  }

  function getItemsFromApi() {
    return new Promise((resolve, reject) => {
      setTimeout(
        resolve({
          items: [
            { text: 'Item 1', value: 'item_1' },
            { text: 'Item 2', value: 'item_2' },
            { text: 'Item 3', value: 'item_3' },
          ],
        }),
        Math.random() * 1000
      )
    })
  }
</script>

<ChildComponent label="ChildComponent w/o other text field:" {items} {value} />

ChildComponent.svelte

<script>
  import { onMount } from 'svelte'

  export let label = ''
  // format: { text:, value: }
  export let items = []
  export let value = ''

  onMount(() => {
    console.log('ChildComponent onMount')
  })

  $: options = buildItems()

  function buildItems() {
    let tempItems = [{ text: 'Please select...', value: '' }, ...items]
    return tempItems
  }

  console.log('ChildComponent items:', { items })
</script>

<div>
  <div>{label}</div>
  <select>
    {#each options as option (Math.random())}
      <option value={option.value} selected={value === option.value}>{option.text}</option>
    {/each}
  </select>
</div>

代码运行后的日志输出:

ChildComponent items: {items: Array(0)}
ChildComponent.svelte:15 ChildComponent onMount
ParentComponent.svelte:9 ParentComponent onMount
ParentComponent.svelte:14 loadItems started:
ParentComponent.svelte:18 loadItems completed: {items: Array(3)}

我原本以为在loadItems中设置items后,ParentComponent会触发重渲染,进而让ChildComponent重渲染,但实际日志里看不到子组件的更新记录,请问我哪里出错了?


解决方法

核心问题分析

  1. 顶层console.log仅执行一次:你在ChildComponent的script顶层写的console.log('ChildComponent items:', { items })只会在组件初始化时运行一次,不会在items更新时触发,这让你误以为组件没重渲染,但实际上Svelte已经在响应数据变化了。
  2. {#each}的key使用错误:用Math.random()作为循环的key是完全错误的——key需要是稳定且唯一的标识,每次渲染生成的随机数会导致Svelte反复销毁重建DOM元素,反而引发问题。
  3. 你不需要强制重渲染:Svelte的响应式系统本来就会自动追踪items的变化,当父组件的items被赋值为新数组时,子组件的items prop会自动更新,进而触发$: options = buildItems()重新计算,组件也会自动更新视图。

修正步骤

  1. 添加响应式日志:把日志放到响应式语句里,这样items变化时会自动打印:
$: console.log('ChildComponent items updated:', { items })
  1. 修正{#each}的key:使用每个选项的唯一值作为key,比如option.value:
{#each options as option (option.value)}
  <option value={option.value} selected={value === option.value}>{option.text}</option>
{/each}
  1. 验证响应式效果:修改后,当API返回数据,items更新时,你会看到新的日志输出,同时select的选项也会自动更新,完全不需要用{#key}强制重渲染。

修正后的ChildComponent代码

<script>
  import { onMount } from 'svelte'

  export let label = ''
  export let items = []
  export let value = ''

  onMount(() => {
    console.log('ChildComponent onMount')
  })

  $: options = buildItems()
  // 响应式日志,items变化时触发
  $: console.log('ChildComponent items updated:', { items })

  function buildItems() {
    let tempItems = [{ text: 'Please select...', value: '' }, ...items]
    return tempItems
  }
</script>

<div>
  <div>{label}</div>
  <select>
    {#each options as option (option.value)}
      <option value={option.value} selected={value === option.value}>{option.text}</option>
    {/each}
  </select>
</div>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 07:00:18