【问题标题】:Svelte Auto Subscription in OnMountOnMount 中的 Svelte 自动订阅
【发布时间】:2022-12-18 12:18:10
【问题描述】:
我有一个简单的 Svelte 项目,它使用存储来保存组件之间的共享数据。
该项目的一部分还引用了 HTML 元素(例如用户名输入字段)。
阅读文档here 和here,我明白我需要:
- 在 onMount 方法中绑定一个 HTML 元素
- 我可以使用自动订阅 ($) 而不是手动订阅,而不必通过销毁时方法
起初,我使用手动订阅,一切正常。当我转向自动订阅时,它不起作用并且值没有得到更新。
下面是一个带有自动订阅的最小示例:
微调器.svelte
<script lang="ts">
import { shouldShowSpinner } from './AppStore';
import { onMount} from 'svelte';
let spinnerDiv
onMount(() => {
spinnerDiv.style.display = $shouldShowSpinner ? "inline-block" : "none"
})
</script>
<main>
<div id="spinner" bind:this={spinnerDiv}>
</div>
</main>
用户输入.svelte:
<script lang="ts">
import { shouldShowSpinner } from './AppStore';
let username = "";
function foo() {
if (username.length === 0) {
return;
}
$shouldShowSpinner = true
...
}
</script>
<main>
<div id="userInput">
<input id="username" type="text" placeholder="Username?" bind:value={username}/>
<button id="search" on:click={foo}>Search</button>
</div>
</main>
我在这里做错了什么吗?
【问题讨论】:
标签:
typescript
svelte
svelte-subscription
onmount
【解决方案1】:
首先,你通常应该不是需要访问元素。如果您使用惯用的 Svelte,问题就会在这里自行解决。
onMount 只运行一次。自动订阅并不意味着代码会重新运行,它只是意味着可以在不泄露订阅的情况下访问商店内容。代码何时/如何运行仍然遵循反应性规则。
要修复代码,您需要像这样的反应式语句:
$: if (spinnerDiv) spinnerDiv.style.display = ...
但这不是如何做到这一点。相反,只需使用{#if},切换类或更改模板中的样式:
{#if $shouldShowSpinner}
<div>...
{/if}
<div class="spinner" class:show={$shouldShowSpinner}>...
<style>
.spinner { display: none; }
.spinner.show { display: inline-block; }
</style>
<div style:display={$shouldShowSpinner ? "inline-block" : "none"}>...
根本不需要使用bind:this。
【解决方案2】:
在您的示例中,您在 onMount 方法中使用自动订阅(由变量名称前的 $ 符号表示)来更新 spinnerDiv 元素的显示样式。但是,这不是自动订阅的正确用法。
自动订阅用于在组件订阅的存储值发生变化时自动更新组件的反应变量。在您的示例中, $shouldShowSpinner 不是 Spinner 组件中的反应变量,因此自动订阅将不起作用。
要解决此问题,您可以在 onDestroy 方法中使用手动订阅和取消订阅,也可以直接在 shouldShowSpinner 存储中更新 spinnerDiv 元素的样式。以下是如何执行此操作的示例:
微调器.svelte
<script lang="ts">
import { shouldShowSpinner } from './AppStore';
import { onMount} from 'svelte';
let spinnerDiv;
// Subscribe to the shouldShowSpinner store
shouldShowSpinner.subscribe(value => {
// Update the spinnerDiv element's display style
// whenever the store value changes
spinnerDiv.style.display = value ? "inline-block" : "none";
});
onMount(() => {
// Set the initial value of the spinnerDiv element's
// display style based on the initial value of
// the shouldShowSpinner store
spinnerDiv.style.display = shouldShowSpinner.get() ? "inline-block" : "none";
});
</script>
<main>
<div id="spinner" bind:this={spinnerDiv}>
</div>
</main>
请注意,在上面的代码中,我们订阅了 shouldShowSpinner 存储,并在存储值发生变化时直接更新 spinnerDiv 元素的样式。我们还根据商店的初始值在 onMount 方法中设置 spinnerDiv 元素样式的初始值。