Svelte组件重复执行异常求助:sdk更新引发循环执行问题
为什么Svelte中异步更新store会触发无关的响应式语句?
问题重现
点击update sdk按钮后,会出现以下异常循环流程:
sdkstore更新,输出sdk updatedTest组件中监听sdk的响应式语句执行,输出sdk TEST!- 异步函数
func延迟后更新valuesstore,输出update balance - 再次输出
sdk TEST!,但sdk并未更新(无sdk updated日志) - 再次执行
func,进入无限循环
而直接点击update balance按钮更新values时,不会触发sdk TEST!语句。
核心原因
问题出在**func的定义方式**和Svelte的响应式渲染机制:
在
+page.svelte的模板中,Test组件的func是内联箭头函数:<Test func={async () => { /* ... */ }} sdk={$sdk} />这个箭头函数会在父组件每次渲染时被重新创建(属于组件渲染上下文的一部分)。
点击
update sdk后:sdk更新为新对象,触发Test组件的响应式块执行func()func是异步函数,延迟后更新valuesstore,导致Provider的val={$values}变化,父组件的slot内容重新渲染- 父组件重新渲染时,新的
func箭头函数被传递给Test组件,触发Test组件的funcprops变化 Test组件中依赖func的响应式块($: { if (sdk) func() })重新执行,再次调用func(),形成循环- 同时,
Test组件因funcprops变化重新渲染,导致所有响应式语句(包括只依赖sdk的$: console.log('sdk TEST!', sdk))被执行
直接点击
update balance时:- 父组件同样会因
values更新重新渲染,func也会被重新创建,但此时Test组件的sdkprops未变化,且没有触发依赖sdk的响应式块执行,因此不会触发sdk TEST!
- 父组件同样会因
解决方法
避免func在每次渲染时重新创建,将其定义在组件的<script>标签中,并通过稳定的引用传递给Test组件:
<script lang="ts"> import { writable, type Writable } from 'svelte/store' import Provider from './provider.svelte' import Test from './test.svelte' const sdk = writable<object>({}) sdk.subscribe((v) => console.log('sdk updated:', v)) let valuesStore: Writable<object> | undefined // 仅当valuesStore变化时才重新创建函数 $: updateBalanceFunc = valuesStore ? async () => { await new Promise((resolve) => setTimeout(resolve, 1500)) console.log('update balance') valuesStore!.update((v) => ({ ...v, balance: 0 })) } : undefined </script> <div class="w-full mx-auto max-w-xl"> <h2 class="text-center text-gray-30 uppercase text-c2">Transfer</h2> <Provider let:values let:val> <!-- 将values赋值给脚本中的变量 --> {valuesStore = values} <form class="flex flex-col gap-4"> <button on:click={() => { console.log('update balance') values.update((v) => ({ ...v, balance: 0 })) }}>update balance</button> <button on:click={() => sdk.set({})}>update sdk</button> <!-- 传递稳定引用的函数 --> <Test func={updateBalanceFunc} sdk={$sdk} /> </form> </Provider> </div>
这样修改后,updateBalanceFunc只会在valuesStore初始化时创建一次,父组件重新渲染时不会改变其引用,因此Test组件的funcprops不会频繁变化,循环和不必要的sdk TEST!输出都会被消除。
内容的提问来源于stack exchange,提问作者Teodorus Nathaniel
相关产品推荐
相关产品推荐

