Svelte使处理单个组件的状态变得非常容易。
但我们如何在不同的组件之间传递状态呢?
使用props传递状态
第一个策略在其他UI框架中很常见,它是使用props来传递状态,将状态向上提升。
当一个组件需要与另一个组件共享数据时,状态可以在组件树中被向上移动,直到这些组件有一个共同的父级。
状态需要向下传递,直到它到达所有需要这个状态信息的组件。
这是用道具完成的,这是我认为最好的一种技术,因为它很简单。
查看Svelte Props教程以了解更多关于props的信息。
上下文API
有些情况下,道具是不实用的。也许两个组件在组件树中距离很远,我们不得不将状态移到顶层组件上。
在这种情况下,可以使用另一种技术,它被称为上下文API,当你想让多个组件与下级组件通信,但又不想传递props时,它是理想的选择。
上下文API由2个函数提供,它们由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 来检索一个键。
如果你想让生活在两个不同组件树中的两个组件进行交流,还有一个工具可以帮助我们:商店。
使用Svelte商店
Svelte存储是一个很好的工具,当组件需要相互交流时,它可以处理你的应用状态,而不需要过多地传递道具。
你必须首先从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() 函数来更新它,它与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服务器)或其他任何可以自主设置的东西。
在这种情况下,我们没有使用writable() 来初始化存储变量,而是使用readable() 。
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派生存储
派生存储允许你创建一个新的存储值,它依赖于一个现有的存储值。
你可以使用由svelte/store 输出的derived() 函数来做到这一点,该函数将现有的存储值作为第一个参数,并将一个函数作为第二个参数,该函数接收该存储值作为其第一个参数。
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}