Svelte 5 在组件之间共享状态(针对初学者)

当你看到 Svelte 5 中的新功能时,你可能会想这样做:

// sharedState.svelte.js

 // This won't work as export! ❌
export const searchState = $state("");

Search for bicycles

如果导出它,上述操作将不起作用 - 原因如下:

“您正在遇到 Svelte 5 中最复杂的部分。反应性如何工作以及编译器如何向您隐藏它。

当你导出数字或字符串等单个值时,Svelte 没有机制来保持反应性,因为 JavaScript 没有提供跟踪该反应性的方法。“

马特·西蒙

非常感谢 Mat Simon 在 Bluesky 帖子中向我解释了这一点 💡

以下是我目前了解到的信息:

对于导出,使用 $state 与对象 - 而不是字符串!

如上所述,我们不能像在单文件组件中那样直接使用字符串(或数字)来导出状态。

// sharedState.svelte.js
// This won't work as export! ❌
export const searchState = $state("");
export const countState = $state(0);

但是,$state() 中的对象的所有属性值都会被 Svelte v5 自动代理。将字符串值包装到对象中可以导出该状态并在文件和组件之间共享:

// sharedState.svelte.js

// Instead of this ...
// export const searchState = $state("");
// ... do this:
export const searchState = $state({ text : "" });

// Instead of this ...
// export const countState = $state(0);
// ... do this:
export const countState = $state({ count : 0 });

这样,`searchState` 和 `countState` 就变成了一个 Svelte 状态代理,带有一个用于 `.text` 属性的 getter 和一个 setter。

当您导入 $state 对象并通过 `.text = "newValue"` 更新属性时,您正在使用 Svelte setter 来更新状态。然后,这将在使用该状态的所有其他位置进行更新:

// App.svelte

import { searchState } from './sharedState.svelte.js'

function handleClick(){
    // uses the automatically created setter
    searchState.text = "bicycles";
}

演示(REPL):非常基本的 $state 示例

您可以选择任何您想要的属性名称,以及每个 $state 对象的多个属性。Svelte 5 会自动处理这些。

// number
export const countState = $state({ count : 999 });
// multiple properties (number, string)
export const anotherState = $state({ id: 123, title: "Hello World!" });
// array
export const tagsState = $state({ selectedTags: [] });

Mat Simon 讲解的完整技术背景:

Svelte 不必代理修改底层对象的每个方法。那样很容易出错。每次 JavaScript 添加新方法时,Svelte 团队都需要立即适应,否则该特定方法的反应性就会中断。实际上,JavaScript 代理非常酷。它们提供 get() 和 set() 陷阱。[..] 这意味着数组(和对象)代理的实际实现非常简单,可以处理数组实现的所有可能方法以及将来可能实现的方法。有关更多详细信息,请参阅 Svelte 文档。

Svelte 文档指出:

如果 $state 与数组或简单对象一起使用,则结果就是深度反应状态代理。

该教程的官方教程是 Universal Reaction。

注意:不要在对象范围之外重新分配对象

当你使用对象时,它们本身也不是状态!理解这一点很重要。如果你在导入后执行以下操作,则会失去反应性:

import { searchState } from './sharedState.svelte.js';
// don't do this!
searchState = {text: "Hello world!"};

Svelte 无法为你处理这个问题。始终使用自动 Svelte getter/setter 进行导出/导入

searchState.text = 'new value';

注意:在同一个作用域内,如果对象是用 $state() 定义的,则可以被覆盖。本文仅介绍导出。

高级:使用类

有多个选项可以定义您的状态对象,如果您需要自定义方法来更新状态中的值,您也可以使用类:https://joyofcode.xyz/how-to-share-state-in-svelte-5#using-classes-for-reactive-state

在组件之间共享状态

因此,我们知道了如何在组件内部导入(和更新)状态,并且我们知道可以使用 `$state` 来使用现成的对象:

// MyComponent.svelte

import { searchState } from './sharedState.svelte.js'

function handleClick(){
    searchState.text = "bicycles";
}

我们甚至可以通过 $props 的属性将 $state 对象作为引用传递下去:

// App.svelte







You're searching for {stateObj.text}

但是,当你在组件内部并想要根据该值进行某些处理时,如何知道应用程序中某个位置的状态发生了变化? 这就是 $derived 和 $derived.by 的用途:





You're searching for {resultString}

简单演示(REPL):在组件之间共享 $state(简单)

与 bind:value 一起使用

您可能已经知道,无需为文本输入编写更改处理函数。您只需在输入文本时使用它来自动更新状态:




确保在将 prop 传递给组件时使用 `bind:`,并在组件中接收 prop 时使用 $bindable()。

与 bind:group 一起使用

可以通过 Svelte 处理多个复选框输入。

要使用具有共享状态的 bind:group,请确保在将 prop 传递给组件时使用 `bind:`:

// state.svelte.js 
export const selectedColorsState = $state({selectedValues: []});

并确保在组件中接收 prop 时使用 $bindable():


  • 简单演示(REPL):使用复选框组组件和 v5 $state & $derived 进行搜索和过滤
  • 但请注意,[Svelte 文档中关于 bind:group 的规定] 指出:

    只有当输入位于同一个 Svelte 组件中时,bind:group 才有效。

    如果您有两个具有不同值供选择的组件,则“bind-group”将不起作用。您应该将其拆分为单独的状态。有关更多详细信息,请参阅 bind:group 不适用于嵌套组件 #2308。

    免责声明:如果您想在 SvelteKit 中使用带有 URL 参数(查询参数)的过滤器复选框,通过“import {page} from`$app/state`and`$derived`”从“page.url.searchParams”派生全局状态可能会更容易。有关更多详细信息,请参阅 https://github.com/mandrasch/austrian-web-dev-companies。

    概括

    在一个文件(一个范围)内使用 `$state()` 与使用 `$state()` 作为导出/导入之间存在很大差异。

    很高兴收到您对本文的(批评性)反馈!🙏

    更多资源

  • Bluesky 讨论:#Svelte v5 - 这是使用 $state(和 $derived)和多个复选框动态过滤数据的最简单方法吗?🤔
  • 在 Svelte 5 中共享状态的不同方法 - Joy of Code + YouTube
  • 使用 Svelte 5、Sveltekit 2、Tailwind、Upstash 构建过滤系统 (2024) - Lawal Adebola
  • Svelte 5 - 全局 $state(将商店转换为 $state 符文) - Svelte Mastery
  • Svelte 5 符文揭秘 (1/4) - 信号反应性基础知识 - Peter Makes Websites Ltd
  • 书呆子讨论:我已经支持 Svelte 3 年多了,但符文却让我苦不堪言。
  • 官方教程:通用反应性
  • 奖励:了解 Svelte 5 中的效果以及何时使用它们
  • 演示:

  • 非常基本的 $state 示例
  • 在组件之间共享 $state(简单)
  • 使用复选框组组件和 v5 $state 和 $derived (WIP) 进行搜索和过滤
  • 完整的 SvelteKit 示例:https://github.com/mandrasch/austrian-web-dev-companies (WIP)
  • 高级:使用 SvelteSet、SvelteMap、SvelteDate 等。

    好的,物体很好,并由 Svelte 自动处理 - 我们明白了。

    但是,以及标准 JavaScript 的更多内置对象呢?如果你对 JavaScript 更有经验,你可能知道还有一些更高级的数据类型(标准内置对象):

  • Set 对象允许您存储任何类型的唯一值,无论是原始值还是对象引用。
  • Map 对象保存键值对并记住键的原始插入顺序。
  • 如果你想要将它们与响应式 $state 一起使用,则需要使用它们对应的 Svelte 包装器

  • 媒体查询
  • SvelteDate
  • SvelteMap
  • 斯维特集
  • 斯维尔特网址
  • SvelteURLSearchParams
  • 之所以有单独的“SvelteSet”和“SvelteMap”类(而不是像处理对象和数组那样自动重写它),是因为他们想在某处画一条线,因为他们无法代理所有可以想到的对象。有关技术细节,请参阅 Reactive Map、Set、Date 和 URL #10263。

    如何使用它?就这么简单:

    ```苗条

    // 共享状态.svelte.js

    从 'svelte/reactivity' 导入 { SvelteSet }

    导出 const selectedColors = new SvelteSet(['red'])

    ```

    但请注意:如果您想输出 `SvelteSet`,请确保使用这个(或使用新的 [$inspect])(https://svelte.dev/docs/svelte/$inspect):

    ```

    {JSON.stringify({selectedColors: [...] selectedColors]})}

    ```

    致谢