在Svelte中处理单个组件的状态非常简单。
但是如何在组件之间传递状态呢?
使用props传递状态
第一种策略是其他UI框架常见的策略,即使用props传递状态,将状态上升。
当一个组件需要与另一个组件共享数据时,可以将状态上升到组件树中的共同父级。
状态需要通过props传递,直到到达需要此状态信息的所有组件。
这是使用props完成的,我认为这是最好的技术,因为它很简单。
有关props的更多信息,请参考Svelte Props教程。
Context API
有些情况下,props并不是一个实用的方法。也许两个组件在组件树中距离太远,我们必须将状态移动到顶层组件。
在这种情况下,可以使用另一种技术,称为context API,它非常适合让多个组件与后代组件进行通信,而无需传递props。
context API由svelte
软件包提供的两个函数提供:getContext
和setContext
。
您可以设置与键关联的上下文对象:
<script>
import { setContext } from 'svelte'
const someObject = {}
setContext('someKey', someObject)
</script>
在另一个组件中,您可以使用getContext来检索分配给键的对象:
<script>
import { getContext } from 'svelte'
const someObject = getContext('someKey')
</script>
您只能在使用setContext的组件或其子代之一中使用getContext检索键。
如果要让两个位于两个不同组件树中的组件进行通信,那么我们还有另外一个工具:stores。
使用Svelte stores
当组件需要相互通信而无需传递太多props时,Svelte stores是一种非常好的工具来处理应用程序的状态。
首先,您必须从svelte/store
中导入可写的writable
:
import { writable } from 'svelte/store'
然后使用writable()
函数创建一个存储变量,将默认值作为第一个参数传递:
const username = writable('Guest')
这可以放在一个单独的文件中,您可以将其导入多个组件中,例如称为store.js
的文件中(它不是一个组件,因此可以是.js
文件而不是.svelte
):
import { writable } from 'svelte/store'
export const username = writable('Guest')
加载此文件的任何其他组件现在都可以访问存储:
<script>
import { username } from './store.js'
</script>
现在可以使用set()
将此变量的值设置为新值,将新值作为第一个参数传递:
username.set('new username')
并且可以使用update()
函数进行更新。update()
与set()
的不同之处在于不仅仅将新值传递给它-您还可以运行一个回调函数,该回调函数将当前值作为其参数传递:
const newUsername = 'new username!'
username.update(existing => newUsername)
在这里您可以添加更多逻辑:
username.update(existing => {
console.log(`Updating username from ${existing} to ${newUsername}`)
return newUsername
})
要一次性获取存储变量的值,可以使用svelte/store
导出的get()
函数:
import { readable, get } from 'svelte/store'
export const username = writable('Guest')
get(username) //'Guest'
要创建一个响应式变量,使其在更改时更新,您可以在存储变量之前加上$
,例如$username
。使用它将使组件在存储的值更改时重新渲染。
Svelte认为
$
是一个保留值,并且会阻止您在与存储值无关的事物上使用它(可能会导致混淆),因此如果您习惯使用$
作为DOM引用的前缀,请不要在Svelte中使用。
另一个选择,如果您需要在变量更改时执行一些逻辑,则可以使用username
的subscribe()
方法:
username.subscribe(newValue => {
console.log(newValue)
})
除了可写存储之外,Svelte还提供了两种特殊类型的存储:可读存储和派生存储。
Svelte可读存储
可读存储是特殊的,因为它们不能从外部更新-没有set()
或update()
方法。而是,在设置初始状态后,它们不能从外部修改。
官方的Svelte文档展示了一个使用定时器更新日期的有趣示例。我可以想到使用定时器从网络获取资源、执行API调用、从文件系统中获取数据(使用本地Node.js服务器)或任何其他可以自主设置的操作。
在这种情况下,我们使用readable()
而不是writable()
来初始化存储变量:
import { readable } from 'svelte/store'
export const count = readable(0)
您可以在默认值之后提供一个函数,该函数负责更新它。此函数接收set
函数以修改该值:
<script>
import { readable } from 'svelte/store'
export const count = readable(0, set => {
setTimeout(() => {
set(1)
}, 1000)
})
</script>
在这种情况下,我们在1秒后将值从0更新为1。
您还可以在此函数中设置间隔:
import { readable, get } from 'svelte/store'
export const count = readable(0, set => {
setInterval(() => {
set(get(count) + 1)
}, 1000)
})
您可以像这样在另一个组件中使用它:
<script>
import { count } from './store.js'
</script>
{$count}
Svelte的派生存储
派生存储允许您创建一个新的存储值,该值依赖于现有存储的值。
您可以使用derived()
函数来实现这一点,该函数由svelte/store
导出,其中第一个参数是现有存储的值,第二个参数是一个函数,该函数将该存储的值作为其第一个参数:
import { writable, derived } from 'svelte/store'
export const username = writable('Guest')
export const welcomeMessage = derived(username, $username => {
return `Welcome ${$username}`
})
您可以像这样在另一个组件中使用它:
<script>
import { username, welcomeMessage } from './store.js'
</script>
{$username}
{$welcomeMessage}