0

复制步骤:

预期

我认为只有在为真$store时才能订阅(使用$:) 。canSubscribe

问题是:如果是假的,为什么要$store订阅?canSubscribe

我错了吗?

4

3 回答 3

3

Svelte 在编译时遍历 AST以确定自动订阅。

即使访问商店的代码无法访问,它也会设置订阅。

例如:

import {foo} from './stores'

let condition = false

if (condition) { $foo }

尽管$foo在技术上无法访问,但直到运行时才会知道。

替代方案:您始终可以使用手动订阅来解决此问题。例子:

import {onDestroy} from 'svelte'
import {myStore} from './stores.js'

// subscribe when component is created
const unsubscribe = myStore.subscribe(value => {
  // this is called anytime the value of myStore changes
})

// make sure to unsubscribe when component is unmounted
onDestroy(unsubscribe)
于 2020-04-17T03:48:19.570 回答
2

在反应式语句中$: canSubscribe && $store,您有一个要评估的表达式。由于它是响应式的,因此 Svelte 必须确定何时重新评估此表达式,这将在两种情况下发生:何时canSubscribe更改,或何时$store更改。所以它必须订阅这两个值,因此您会看到您立即在代码中获得订阅者。

请注意,我们经常canDoSomething && canDoSomething()在 JavaScript 中这样做,但这并不是 100% 相同,if (canDoSomething) { canDoSomething()}只是在大多数情况下,效果相同。

于 2020-04-16T05:59:05.170 回答
2

我错了吗?

是的。不过不要责怪自己,你的期望对我来说似乎是合乎逻辑的。但这不是它的工作方式。

作为一般规则,如果$您的组件代码中某处有一个前缀变量,那么它必须是一个 store,并且它将在组件创建时立即订阅,并在组件被销毁时取消订阅。

不过最近引入了这个规则的一个小例外(使用这个 PR)。如果你想了解整个讨论,我会让你沿着兔子洞的踪迹走。关键在于,现在,商店订阅必须是商店无效(即,nullundefined-- 参见此评论)。

这意味着如果需要,现在可以侵入您预期的行为。我们回到这个话题。

如果 canSubscribe 为 false,为什么要订阅 $store?

因为商店是立即订阅的。从上面链接的问题的讨论中,我的理解是,它是为了性能(字节大小)和健全性(如果有人试图订阅不是商店的东西,则会快速而明显地失败)。我感觉合理。

现在,回到您没有问的问题:如何仅在需要时/如果需要时订阅?仅在需要时将商店放入自动订阅的变量中,否则将其保持为空。

不要这样做:

$: started && $store

改为这样做:

$: proxyStore = started ? store : null
$: console.log($proxyStore)

完整示例(REPL):

<script>
    import { writable } from 'svelte/store'

    const state1 = { subscribed: 0, unsubscribed: 0 }

    const store1 = writable(42, () => {
        state1.subscribed++
        return () => {
            state1.unsubscribed++
        }
    })

    const state2 = { subscribed: 0, unsubscribed: 0 }

    const store2 = writable(43, () => {
        state2.subscribed++
        return () => {
            state2.unsubscribed++
        }
    })

    let started = false

    $: started && $store1

    $: targetStore = started ? store2 : null

    $: $targetStore
</script>

<pre>
started = {started}
store1 = {$store1} {JSON.stringify(state1)}
store2 = {$targetStore} {JSON.stringify(state2)}
</pre>

<button on:click={() => {started = !started}}>
    {started ? 'Start' : 'Stop'}
</button>
于 2020-04-19T23:12:34.653 回答