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

Max*_*ess 4 svelte sveltekit

我正在尝试将 svelte SPA 迁移到 Sveltekit 中。

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

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

正如您在此示例中所看到的,该序列相当简单: 应用程序状态 1) 应用程序显示 CompA 和 CommonComp 应用程序状态 2) CompA 在安装后立即触发 doSomething 函数 应用程序状态 3) 应用程序然后调用 commonComp.setTitle 并在中显示 CompB CompA所在地

在 SvelteKit 中,我很难做类似的事情,因为我不明白如何将数据从带槽的子组件传递到包含槽的组件,反之亦然。无论如何,这导致我进行了这次尝试:

  1. 我需要2条路线:

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

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

/src/routes/__layout.svelte

<slot />
<CommonComp />
Run Code Online (Sandbox Code Playgroud)

/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} />
Run Code Online (Sandbox Code Playgroud)

/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>
Run Code Online (Sandbox Code Playgroud)

/src/routes/PageB.svelte

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

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

<CompB  />
Run Code Online (Sandbox Code Playgroud)

/src/lib/CompB.svelte

<p>Component B</p>
Run Code Online (Sandbox Code Playgroud)

/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>
Run Code Online (Sandbox Code Playgroud)

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

Jax*_*axx 6

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

如果导出常量、类或函数,则它从组件外部是只读的。然而,函数表达式是有效的 props。

只读 props 可以作为元素上的属性进行访问,并使用 bind:this 语法绑定到组件。

这意味着您必须创建对CommonCompusing的引用bind:this,然后传递它才能setTitle调用该方法。这将是支柱向下钻取,并且从布局的 向下钻取会更加令人头疼slot。我根本不推荐这样做。

一种途径是利用load和 功能,通过在 PageB 的加载函数中设置对象的属性,然后订阅内部stuff提供的存储以响应式设置标题:ccTitlestuffpage$app/storesCommonComp

$: title = $page.stuff.ccTitle || "Common Component"
Run Code Online (Sandbox Code Playgroud)

但是,因为您是stuffloadPageB 的功能进行设置的,所以它不是很灵活(在页面实际加载之前load从模块部分执行,因此您无法从实际的页面脚本进行交互)。stuff

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

最后,你似乎不愿意去的商店路线将是最简单、最直接的选择。这家title商店基本到了极致:

import { writable } from 'svelte/store';

const title = writable('Common Component');

export default title;
Run Code Online (Sandbox Code Playgroud)

使用商店也是如此CommonComp

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

<p>{$title}</p>
Run Code Online (Sandbox Code Playgroud)

从 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 />
Run Code Online (Sandbox Code Playgroud)

这比load上面提到的方法以及潜在的深入研究方法要简单和干净得多。商店不需要复杂,并且对于跨页面和组件进行通信非常有帮助。

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

更新/编辑:

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

是使用该方法的示例。