布局、路由和组件之间的 Sveltekit 通信

Sveltekit communication between layout, routes and components

我正在尝试将 svelte SPA 移至 Sveltekit。

在我的 SPA 中,通信模式就是我所说的“控制器组件”,它负责显示某些组件、侦听它们的事件并相应地更新应用程序。总的来说,它看起来像这样的 REPL:

https://svelte.dev/repl/47bd3f8004624a3c95653b1f1aefd8ee?version=3.46.4

如您在此示例中所见,序列非常简单: App 状态 1) App 显示 CompA 和 CommonComp App state 2) CompA刚挂载后触发doSomething函数 应用程序状态 3) 应用程序然后调用 commonComp.setTitle 并显示 CompB 代替 CompA

在 SvelteKit 中,我很难做类似的事情,因为我不明白如何将数据从带插槽的子组件传递到包含插槽的组件,反之亦然。 不管怎样,这让我做出了这样的尝试:

  1. 我需要2条路线:

    • PageA.svelte 当显示 CompA 和 CommonComp 时
    • PageB.svelte CompB & CommonComp 显示时
  2. 因为 CommonComp 在每个州始终可见,我认为它应该驻留在 __layout.svelte 文件中。

...这让我看到了下面的草稿,其中的评论解释了我遇到的访问问题。

/src/routes/__layout.svelte

<slot />
<CommonComp />

/src/routes/PageA.svelte

<script>
  import { goto } from "$app/navigation";
  import CompA from "$lib/CompA.svelte";

  function handleDoSomethingFinished() {
    goto("/test/pageB");
  }
</script>

<CompA on:do_something_finished={handleDoSomethingFinished} />

/src/lib/CompA.svelte

<script>
  import { onMount } from "svelte";
  import { createEventDispatcher } from "svelte";

  const dispatch = createEventDispatcher();

  onMount(() => {
    setTimeout(() => dispatch("do_something_finished"), 3000);
  });
</script>

<p>Component A</p>

/src/routes/PageB.svelte

<script>
  import CompB from "$lib/CompB.svelte";

  // How to call CommonComp.setTitle function from here ?
 
</script>

<CompB  />

/src/lib/CompB.svelte

<p>Component B</p>

/src/lib/CommonComp.svelte

<script>
  let title = "Common Component";
  import { createEventDispatcher } from "svelte";

  const dispatch = createEventDispatcher();

  export function setTitle(t) {
    title = t;
    dispatch("title-modified");
  }
</script>

<p>{title}</p>

我想我可能已经尝试过共享一些商店并将它们的值检查到响应式语句中以触发适当的操作但是当我想到它时,我看到了一堆蠕虫所以我在这里遗漏了一些东西。感谢您的帮助。

这有点复杂。根据 export 上的 Svelte 文档:

If you export a const, class or function, it is readonly from outside the component. Function expressions are valid props, however.

Readonly props can be accessed as properties on the element, tied to the component using bind:this syntax.

意味着您必须使用 bind:this 创建对 CommonComp 的引用,然后传递它以便在其上调用 setTitle 方法。这将是向下钻取的支柱,从布局的 slot 向下钻取会带来额外的麻烦。我根本不推荐这样做。

一种方法是利用 load facility and stuff,通过在 stuff object 上设置 属性,例如 ccTitle PageB 的加载函数,然后在 CommonComp 内订阅 $app/stores 提供的 page 商店以响应式设置标题:

$: title = $page.stuff.ccTitle || "Common Component"

但是,因为你是通过PageB的load函数设置stuff,所以不是很灵活(load是从模块部分 prior 到实际加载的页面执行,因此您无法从实际页面脚本与 stuff 交互。

This 是上述方法的一个简单示例。

最终,你似乎不愿意下去的商店路线将是最简单直接的选择。 title店铺基础到极致:

import { writable } from 'svelte/store';

const title = writable('Common Component');

export default title;

使用 CommonComp 中的商店也是如此:

<script>
  import title from "$lib/stores/title";
</script>

<p>{$title}</p>

从 PageB 设置新标题也是如此:

<script>
  import CompB from "$lib/CompB.svelte";
  import title from "$lib/stores/title";

  $title = "Custom Title";
  // or say you wanted to set it reactively based on a 'foo' variable:
  // $: $title = foo
</script>

<CompB />

这比上面提到的 load 方法以及 would-be 向下钻取方法要简单和清晰得多。商店 不需要 复杂,并且对跨页面和组件进行交流非常有帮助。

Here 是使用商店方法的简单示例。

更新/编辑:

如果你想封装你常用的组件方法,你也可以使用上面提到的存储方法,但不是存储标题,而是存储对你的 CommonComp 实例的引用,从而提供一个表示从订阅商店的任何页面或组件调用 $commonComp.setTitle()

Here 是使用该方法的示例。

组件的结构对于更容易绑定值是最重要的。