在Svelte中处理单个组件的状态非常简单。

但是如何在组件之间传递状态呢?

使用props传递状态

第一种策略是其他UI框架常见的策略,即使用props传递状态,将状态上升。

当一个组件需要与另一个组件共享数据时,可以将状态上升到组件树中的共同父级。

状态需要通过props传递,直到到达需要此状态信息的所有组件。

这是使用props完成的,我认为这是最好的技术,因为它很简单。

有关props的更多信息,请参考Svelte Props教程。

Context API

有些情况下,props并不是一个实用的方法。也许两个组件在组件树中距离太远,我们必须将状态移动到顶层组件。

在这种情况下,可以使用另一种技术,称为context API,它非常适合让多个组件与后代组件进行通信,而无需传递props。

context API由svelte软件包提供的两个函数提供:getContextsetContext

您可以设置与键关联的上下文对象:

<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中使用。

另一个选择,如果您需要在变量更改时执行一些逻辑,则可以使用usernamesubscribe()方法:

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}