Svelte 商店有条件的自动订阅
Svelte store conditional auto subscription
复制步骤:
在此处转到 REPL:https://svelte.dev/repl/27a2cff81ebb4000970eee32747cc6c2?version=3.20.1
打开控制台
取消注释第 27 行 ($: canSubscribe && $store
)
预计:
我认为 $store
可以订阅(使用 $:
)只有 如果 canSubscribe
为真。
问题是:如果 canSubscribe
为假,为什么 $store
订阅?
我错了吗?
在反应式语句 $: canSubscribe && $store
中,您有一个要计算的表达式。由于它是反应式的,Svelte 必须确定何时重新评估此表达式,这将在两种情况下发生:当 canSubscribe
更改时,或当 $store
更改时。因此,它必须对这两个值进行 订阅,因此您会立即在代码中看到订阅者。
请注意,我们经常在 JavaScript 中执行 canDoSomething && canDoSomething()
,但这与执行 if (canDoSomething) { canDoSomething()}
并非 100% 相同,只是在大多数情况下具有相同的效果。
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)
Am I wrong?
是的。不要责怪自己,你的期望对我来说似乎是合乎逻辑的。但这不是它的工作方式。
作为一般规则,如果在您的组件代码中某处有一个 $
前缀变量,那么 它必须是一个商店 ,并且它将被正确订阅离开组件创建,并在组件销毁时取消订阅。
虽然最近引入了此规则的一个小例外(this PR). I'll let you follow the trail down the rabbit hole if you want to know the whole discussion. The crux of it is that, now, a store subscription must be a store or nullish (that is, null
or undefined
-- see this comment)。
这意味着如果需要,现在可以通过黑客攻击达到您预期的行为。我们回到这个。
Why $store subscribe if canSubscribe is false?
因为商店马上就被订阅了。从上面链接的问题中的讨论来看,我的理解是它是为了性能(字节大小)和健全性(如果有人试图订阅不是商店的东西,就会快速失败并且明显地失败)。对我来说很有意义。
现在,回到您没有问的问题:如何 订阅仅 when/if 需要?仅在需要时将商店放入自动订阅变量中,否则保持空值。
不做:
$: 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>
复制步骤:
在此处转到 REPL:https://svelte.dev/repl/27a2cff81ebb4000970eee32747cc6c2?version=3.20.1
打开控制台
取消注释第 27 行 (
$: canSubscribe && $store
)
预计:
我认为 $store
可以订阅(使用 $:
)只有 如果 canSubscribe
为真。
问题是:如果 canSubscribe
为假,为什么 $store
订阅?
我错了吗?
在反应式语句 $: canSubscribe && $store
中,您有一个要计算的表达式。由于它是反应式的,Svelte 必须确定何时重新评估此表达式,这将在两种情况下发生:当 canSubscribe
更改时,或当 $store
更改时。因此,它必须对这两个值进行 订阅,因此您会立即在代码中看到订阅者。
请注意,我们经常在 JavaScript 中执行 canDoSomething && canDoSomething()
,但这与执行 if (canDoSomething) { canDoSomething()}
并非 100% 相同,只是在大多数情况下具有相同的效果。
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)
Am I wrong?
是的。不要责怪自己,你的期望对我来说似乎是合乎逻辑的。但这不是它的工作方式。
作为一般规则,如果在您的组件代码中某处有一个 $
前缀变量,那么 它必须是一个商店 ,并且它将被正确订阅离开组件创建,并在组件销毁时取消订阅。
虽然最近引入了此规则的一个小例外(this PR). I'll let you follow the trail down the rabbit hole if you want to know the whole discussion. The crux of it is that, now, a store subscription must be a store or nullish (that is, null
or undefined
-- see this comment)。
这意味着如果需要,现在可以通过黑客攻击达到您预期的行为。我们回到这个。
Why $store subscribe if canSubscribe is false?
因为商店马上就被订阅了。从上面链接的问题中的讨论来看,我的理解是它是为了性能(字节大小)和健全性(如果有人试图订阅不是商店的东西,就会快速失败并且明显地失败)。对我来说很有意义。
现在,回到您没有问的问题:如何 订阅仅 when/if 需要?仅在需要时将商店放入自动订阅变量中,否则保持空值。
不做:
$: 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>