效果是当状态更新时运行的函数,可用于调用第三方库、在`元素上绘图或发起网络请求。它们仅在浏览器中运行,不在服务器端渲染期间运行。` 一般来说,你应*不要*在效果中更新状态,因为这会使代码更加复杂,并常常导致无限循环更新。如果你发现自己正在这样做,请参阅[何时不应使用`$effect`](#When-not-to-use-$effect)以了解替代方法。 您可以使用`$effect`符文([演示](/playground/untitled#H4sIAAAAAAAAE31S246bMBD9lZF3pSRSAqTVvrCAVPUP2sdSKY4ZwJJjkD0hSVH-vbINuWxXfQH5zMyZc2ZmZLVUaFn6a2R06ZGlHmBrpvnBvb71fWQHVOSwPbf4GS46TajJspRlVhjZU1HqkhQSWPkHIYdXS5xw-Zas3ueI6FRn7qHFS11_xSRZhIxbFtcDtw7SJb1iXaOg5XIFeQGjzyPRaevYNOGZIJ8qogbpe8CWiy_VzEpTXiQUcvPDkSVrSNZz1UlW1N5eLcqmpdXUvaQ4BmqlhZNUCgxuzFHDqUWNAxrYeUM76AzsnOsdiJbrBp_71lKpn3RRbii-4P3f-IMsRxS-wcDV_bL4PmSdBa2wl7pKnbp8DMgVvJm8ZNskKRkEM_OzyOKQFkgqOYBQ3Nq89Ns0nbIl81vMFN-jKoLMTOr-SOBOJS-Z8f5Y6D1wdcR8dFqvEBdetK-PHwj-z-cH8oHPY54wRJ8Ys7iSQ3Bg3VA9azQbmC9k35kKzYa6PoVtfwbbKVnBixBiGn7Pq0rqJoUtHiCZwAM3jdTPWCVtr_glhVrhecIa3vuksJ_b7TqFs4DPyriSjd5IwoNNQaAmNI-ESfR2p8zimzvN1swdCkvJHPH6-_oX8o1SgcIDAAA=))创建效果: ```svelte ``` 當 Svelte 运行效果函数時,它會追踪哪些狀態(以及導出狀態)被訪問(除非在 [`untrack`](svelte#untrack) 內部訪問),當該狀態之後發生變化時,會重新運行該函數。 > \[!注意\] 如果您难以理解为什么您的 `$effect` 重新运行或没有运行,请参阅 [理解依赖关系](#Understanding-dependencies)。与您从 Svelte 4 过来可能习惯的 `$:` 块相比,效果触发方式不同。 ### 理解生命周期 您的效果在组件挂载到 DOM 之后运行,并在状态变化后的一个[微任务](https://developer.mozilla.org/en-US/docs/Web/API/HTML_DOM_API/Microtask_guide)中运行。重新运行是分批进行的(即在同一时刻更改`颜色`和`大小`不会导致两次单独的运行),并且发生在任何 DOM 更新之后。 您可以在任何地方使用`$effect`,而不仅仅是组件的顶层,只要在父级效果运行时调用即可。 > \[!注意\] Svelte 在模板内部使用效果来表示逻辑和表达式 — 这就是当 `

hello {name}!

` 更新时 `name` 发生变化的方式。 一个效果可以返回一个在效果重新运行前立即执行的 *拆卸函数*([演示](/playground/untitled#H4sIAAAAAAAAE42SQVODMBCF_8pOxkPRKq3HCsx49K4n64xpskjGkDDJ0tph-O8uINo6HjxB3u7HvrehE07WKDbiyZEhi1osRWksRrF57gQdm6E2CKx_dd43zU3co6VB28mIf-nKO0JH_BmRRRVMQ8XWbXkAgfKtI8jhIpIkXKySu7lSG2tNRGZ1_GlYr1ZTD3ddYFmiosUigbyAbpC2lKbwWJkIB8ZhhxBQBWRSw6FCh3sM8GrYTthL-wqqku4N44TyqEgwF3lmRHr4Op0PGXoH31c5rO8mqV-eOZ49bikgtcHBL55tmhIkEMqg_cFB2TpFxjtg703we6NRL8HQFCS07oSUCZi6Rm04lz1yytIHBKoQpo1w6Gsm4gmyS8b8Y5PydeMdX8gwS2Ok4I-ov5NZtvQde95GMsccn_1wzNKfu3RZtS66cSl9lvL7qO1aIk7knbJGvefdtIOzi73M4bYvovUHDFk6AcX_0HRESxnpBOW_jfCDxIZCi_1L_wm4xGQ60wIAAA==))。 ```svelte

{count}

``` 拆解函数在效果被销毁时也会运行,这发生在其父级被销毁(例如,组件被卸载)或父级效果重新运行时。 ### 理解依赖关系 `$effect` 自动获取其函数体内(包括间接地,通过函数调用)同步读取的任何响应式值(`$state`、`$derived`、`$props`)并将它们注册为依赖项。当这些依赖项发生变化时,`$effect` 将安排重新运行。 如果直接在 `$effect` 中使用 `$state` 和 `$derived`(例如,在创建一个 [reactive 类](https://svelte.dev/docs/svelte/$state#Classes) 时),这些值将 *不会被* 作为依赖项处理。 值在异步读取*(例如,在`await`之后或`setTimeout`内部)时不会被跟踪。在此,当`color`改变时,画布将被重绘,但`size`改变时不会([演示](/playground/untitled#H4sIAAAAAAAAE31T246bMBD9lZF3pWSlBEirfaEQqdo_2PatVIpjBrDkGGQPJGnEv1e2IZfVal-wfHzmzJyZ4cIqqdCy9M-F0blDlnqArZjmB3f72XWRHVCRw_bc4me4aDWhJstSlllhZEfbQhekkMDKfwg5PFvihMvX5OXH_CJa1Zrb0-Kpqr5jkiwC48rieuDWQbqgZ6wqFLRcvkC-hYvnkWi1dWqa8ESQTxFRjfQWsOXiWzmr0sSLhEJu3p1YsoJkNUcdZUnN9dagrBu6FVRQHAM10sJRKgUG16bXcGxQ44AGdt7SDkTDdY02iqLHnJVU6hedlWuIp94JW6Tf8oBt_8GdTxlF0b4n0C35ZLBzXb3mmYn3ae6cOW74zj0YVzDNYXRHFt9mprNgHfZSl6mzml8CMoLvTV6wTZIUDEJv5us2iwMtiJRyAKG4tXnhl8O0yhbML0Wm-B7VNlSSSd31BG7z8oIZZ6dgIffAVY_5xdU9Qrz1Bnx8fCfwtZ7v8Qc9j3nB8PqgmMWlHIID6-bkVaPZwDySfWtKNGtquxQ23Qlsq2QJT0KIqb8dL0up6xQ2eIBkAg_c1FI_YqW0neLnFCqFpwmreedJYT7XX8FVOBfwWRhXstZrSXiwKQjUhOZeMIleb5JZfHWn2Yq5pWEpmR7Hv-N_wEqT8hEEAAA=)):* ```ts // @filename: index.ts declare let canvas: { width: number; height: number; getContext(type: '2d', options?: CanvasRenderingContext2DSettings): CanvasRenderingContext2D; }; declare let color: string; declare let size: number; // ---cut--- $effect(() => { const context = canvas.getContext('2d'); context.clearRect(0, 0, canvas.width, canvas.height); // this will re-run whenever `color` changes... context.fillStyle = color; setTimeout(() => { // ...but not when `size` changes context.fillRect(0, 0, size, size); }, 0); }); ``` 仅当读取的对象发生变化时,效果才会重新运行,而不是当它内部的属性发生变化时。(如果您想在开发时观察对象内部的更改,可以使用[`$inspect`]($inspect)。) ```svelte

{state.value} doubled is {derived.value}

``` 一个效果只依赖于它上次运行时读取的值。这对具有条件代码的效果有有趣的含义。 例如,如果以下代码片段中的`condition`是`true`,则`if`块内的代码将执行,并且`color`将被评估。因此,对`condition`或`color`[的更改将导致效果重新运行。](/playground/untitled#H4sIAAAAAAAAE21RQW6DMBD8ytaNBJHaJFLViwNIVZ8RcnBgXVk1xsILTYT4e20TQg89IOPZ2fHM7siMaJBx9tmaWpFqjQNlAKXEihx7YVJpdIyfRkY3G4gB8Pi97cPanRtQU8AuwuF_eNUaQuPlOMtc1SlLRWlKUo1tOwJflUikQHZtA0klzCDc64Imx0ANn8bInV1CDhtHgjClrsftcSXotluLybOUb3g4JJHhOZs5WZpuIS9gjNqkJKQP5e2ClrR4SMdZ13E4xZ8zTPOTJU2A2uE_PQ9COCI926_hTVarIU4hu_REPlBrKq2q73ycrf1N-vS4TMUsulaVg3EtR8H9rFgsg8uUsT1B2F9eshigZHBRpuaD0D3mY8Qm2BfB5N2YyRzdNEYVDy0Ja-WsFjcOUuP1HvFLWA6H3XuHTUSmmDV2--0TXonxsKbp7G9C6R__NONS-MFNvxj_d6mBAgAA) 相反,如果 `条件` 是 `假`,`颜色` 将不会被评估,并且效果将 *仅* 在 `条件` 发生变化时再次运行。 ```ts // @filename: ambient.d.ts declare module 'canvas-confetti' { interface ConfettiOptions { colors: string[]; } function confetti(opts?: ConfettiOptions): void; export default confetti; } // @filename: index.js // ---cut--- import confetti from 'canvas-confetti'; let condition = $state(true); let color = $state('#ff3e00'); $effect(() => { if (condition) { confetti({ colors: [color] }); } else { confetti(); } }); ``` ## `$effect.pre` 在罕见情况下,您可能需要在 DOM 更新之前运行代码*。*为此,我们可以使用`$effect.pre`符文: ```svelte
{#each messages as message}

{message}

{/each}
``` 除了时间之外,`$effect.pre` 的功能与 `$effect` 完全相同。 ## `$effect.tracking` The `$effect.tracking` 符文是一个高级功能,它告诉您代码是否在跟踪上下文中运行,例如在效果或您的模板中([示例](/playground/untitled#H4sIAAAAAAAACn3PwYrCMBDG8VeZDYIt2PYeY8Dn2HrIhqkU08nQjItS-u6buAt7UDzmz8ePyaKGMWBS-nNRcmdU-hHUTpGbyuvI3KZvDFLal0v4qvtIgiSZUSb5eWSxPfWSc4oB2xDP1XYk8HHiSHkICeXKeruDDQ4Demlldv4y0rmq6z10HQwuJMxGVv4mVVXDwcJS0jP9u3knynwtoKz1vifT_Z9Jhm0WBCcOTlDD8kyspmML5qNpHg40jc3fFryJ0iWsp_UHgz3180oBAAA=)): ```svelte

in template: {$effect.tracking()}

``` 用于实现如 [`createSubscriber`](/docs/svelte/svelte-reactivity#createSubscriber) 这样的抽象,这将创建监听器以更新响应式值,但 *仅* 当这些值正在被跟踪(而不是,例如,在事件处理程序内部读取)时。 ## `$effect.pending` 当在组件中使用[`await`](await-expressions)时,`$effect.pending()`函数告诉你当前[边界](svelte-boundary)(不包括子边界[示例](/playground/untitled#H4sIAAAAAAAAE3WRMU_DMBCF_8rJdHDUqilILGkaiY2RgY0yOPYZWbiOFV8IleX_jpMUEAIWS_7u-d27c2ROnJBV7B6t7WDsequAozKEqmAbpo3FwKqnyOjsJ90EMr-8uvN-G97Q0sRaEfAvLjtH6CjbsDrI3nhqju5IFgkEHGAVSBDy62L_SdtvejPTzEU4Owl6cJJM50AoxcUG2gLiVM31URgChyM89N3JBORcF3BoICA9mhN2A3G9gdvdrij2UJYgejLaSCMsKLTivNj0SEOf7WEN7ZwnHV1dfqd2dTsQ5QCdk9bI10PkcxexXqcmH3W51Jt_le2kbH8os9Y3UaTcNLYpDx-Xab6GTHXpZ128MhpWqDVK2np0yrgXXqQpaLa4APDLBkIF8bd2sYql0Sn_DeE7sYr6AdNzvgljR-MUq7SwAdMHeUtgHR4CAAA=))中有多少个待处理的承诺: ```svelte

{a} + {b} = {await add(a, b)}

{#if $effect.pending()}

pending promises: {$effect.pending()}

{/if} ``` ## `$effect.root` The `$effect.root` 符文是一个高级功能,它创建一个非跟踪作用域,不会自动清理。这对于您想手动控制嵌套效果非常有用。此符文还允许在组件初始化阶段之外创建效果。 ```js const destroy = $effect.root(() => { $effect(() => { // setup }); return () => { // cleanup }; }); // later... destroy(); ``` ## 當何時不使用`$effect` 通常,`$effect`最好被视为一种逃生门——适用于像分析和对 DOM 的直接操作这样的用途——而不是你应该频繁使用的工具。特别是,避免用它来同步状态。而不是这样做…… ```svelte ``` ...这样做: ```svelte ``` > \[!注意\] 对于比简单表达式如`count * 2`更复杂的情况,您也可以使用`$derived.by`。 如果您使用效果是因为您想要能够重新分配派生值(例如构建乐观的用户界面),请注意,从 Svelte 5.25 开始,[派生值]($derived#Overriding-derived-values)可以直接覆盖。 您可能会想用一些复杂的效果将一个值链接到另一个值。以下示例显示了两个输入:“花费的金额”和“剩余的金额”,它们彼此相连。如果您更新其中一个,另一个也应该相应更新。不要为此使用效果([演示](/playground/untitled#H4sIAAAAAAAAE5WRTWrDMBCFryKGLBJoY3fRjWIHeoiu6i6UZBwEY0VE49TB-O6VxrFTSih0qe_Ne_OjHpxpEDS8O7ZMeIAnqC1hAP3RA1990hKI_Fb55v06XJA4sZ0J-IjvT47RcYyBIuzP1vO2chVHHFjxiQ2pUr3k-SZRQlbBx_LIFoEN4zJfzQph_UMQr4hRXmBd456Xy5Uqt6pPKHmkfmzyPAZL2PCnbRpg8qWYu63I7lu4gswOSRYqrPNt3CgeqqzgbNwRK1A76w76YqjFspfcQTWmK3vJHlQm1puSTVSeqdOc_r9GaeCHfUSY26TXry6Br4RSK3C6yMEGT-aqVU3YbUZ2NF6rfP2KzXgbuYzY46czdgyazy0On_FlLH3F-UDXhgIO35UGlA1rAgAA)): ```svelte ``` 相反,使用`oninput`回调或——更好的选择——尽可能使用[函数绑定](bind#Function-bindings)([演示](/playground/untitled#H4sIAAAAAAAAE5VRvW7CMBB-FcvqECQK6dDFJEgsnfoGTQdDLsjSxVjxhYKivHvPBwFUsXS8774_nwftbQva6I_e78gdvNo6Xzu_j3quG4cQtfkaNJ1DIiWA8atkE8IiHgEpYVsb4Rm-O3gCT2yji7jrXKB15StiOJKiA1lUpXrL81VCEUjFwHTGXiJZgiyf3TYIjSxq6NwR6uyifr0ohMbEZnpHH2rWf7ImS8KZGtK6osl_UqelRIyVL5b3ir5AuwWUtoXzoee6fIWy0p31e6i0XMocLfZQDuI6qtaeykGcR7UU6XWznFAZU9LN_X9B2UyVayk9f3ji0-REugen6U9upDOCcAWcLlS7GNCejWoQTqsLtrfBqHzxDu3DrUTOf0xwIm2o62H85sk6_OHG2jQWI4y_3byXXGMCAAA=)): ```svelte ``` 如果您绝对需要在效果中更新`$state`并遇到无限循环,因为您读取和写入相同的`$state`,请使用[untrack](svelte#untrack)。