Svelte中的跨组件状态管理

471 阅读4分钟

Svelte使处理单个组件的状态变得非常容易。

但我们如何在不同的组件之间传递状态呢?

使用props传递状态

第一个策略在其他UI框架中很常见,它是使用props来传递状态,将状态向上提升

当一个组件需要与另一个组件共享数据时,状态可以在组件树中被向上移动,直到这些组件有一个共同的父级。

状态需要向下传递,直到它到达所有需要这个状态信息的组件。

这是用道具完成的,这是我认为最好的一种技术,因为它很简单。

查看Svelte Props教程以了解更多关于props的信息。

上下文API

有些情况下,道具是不实用的。也许两个组件在组件树中距离很远,我们不得不将状态移到顶层组件上。

在这种情况下,可以使用另一种技术,它被称为上下文API,当你想让多个组件与下级组件通信,但又不想传递props时,它是理想的选择。

上下文API由2个函数提供,它们由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 来检索一个键。

如果你想让生活在两个不同组件树中的两个组件进行交流,还有一个工具可以帮助我们:商店

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

另一个选择,如果你需要在变量变化时执行一些逻辑,最适合使用usernamesubscribe() 方法。

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}