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重渲染,但实际日志里看不到子组件的更新记录,请问我哪里出错了?
解决方法
核心问题分析
- 顶层console.log仅执行一次:你在
ChildComponent的script顶层写的console.log('ChildComponent items:', { items })只会在组件初始化时运行一次,不会在items更新时触发,这让你误以为组件没重渲染,但实际上Svelte已经在响应数据变化了。 - {#each}的key使用错误:用
Math.random()作为循环的key是完全错误的——key需要是稳定且唯一的标识,每次渲染生成的随机数会导致Svelte反复销毁重建DOM元素,反而引发问题。 - 你不需要强制重渲染:Svelte的响应式系统本来就会自动追踪
items的变化,当父组件的items被赋值为新数组时,子组件的itemsprop会自动更新,进而触发$: options = buildItems()重新计算,组件也会自动更新视图。
修正步骤
- 添加响应式日志:把日志放到响应式语句里,这样
items变化时会自动打印:
$: console.log('ChildComponent items updated:', { items })
- 修正{#each}的key:使用每个选项的唯一值作为key,比如
option.value:
{#each options as option (option.value)} <option value={option.value} selected={value === option.value}>{option.text}</option> {/each}
- 验证响应式效果:修改后,当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
相关产品推荐
相关产品推荐

