Svelte - 在常规道具传递上使用 Context API (setContext/getContext)

Max*_*ore 2 javascript svelte

这是一个简单的例子:

<script>
    import Button from './Button.svelte';

    let text = 'Click me!';
    let sayHello = () => alert('Hello!');
</script>

<Button {text} {sayHello}/>
<Button {text} {sayHello}/>
<Button {text} {sayHello}/>
Run Code Online (Sandbox Code Playgroud)

如果我做对了,因为可能有很多<Button {text} {sayHello}/>,所以省略以某种方式传递的道具会很好

这里来上下文API

<script>
    import Button from './Button.svelte';
    import { setContext } from 'svelte';
    import { text, sayHello } from './data.js';

    setContext(text, 'Click me!');
    setContext(sayHello, () => alert('Hello!'));
</script>

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

并且在某处./Button.sveltegetContext()使用等


那么,省略类似道具传递的能力是使用 Svelte 的Context API的唯一原因吗?

rix*_*ixo 11

那么,省略类似 props 传递的能力是使用 Svelte 的 Context API 的唯一原因吗?

不,而且,在我看来,它甚至不是上下文的一个很好的用法。

这里的问题是您混淆了父组件与其按钮子组件之间的数据关系。

使用道具,按钮需要什么数据以及它来自哪里是明确的。另一方面,通过上下文,您一次只能看到关系的一面。在父级中,您看不到数据是如何使用的(甚至根本看不到数据还在使用中)。孩子也是一样,你看不到它来自哪里。

此外,例如,错误输入道具或删除仍然需要的道具,将导致立即可见的开发警告(充满问题的确切位置)。使用上下文,您最终可能会得到一个未定义的值,该值会产生奇怪的运行时行为,但很难追踪。

因此,虽然在编码过程中节省一点打字时间似乎是个好主意,但它实际上增加了代码的复杂性,并可能欺骗您并给您一个以后头疼得要命……如果你想要我的意见,这不是一个好的权衡。

然而,在某些情况下,道具不是一种选择。也就是说,当数据消费者组件不是数据提供者组件的直接子级时。

例如,您的应用程序中可能有某种用户会话。它很可能会存储在靠近组件树根部的组件中(例如 App),但在组件中需要更深层次的嵌套。例如,在显示用户姓名的组件中。或者页面中的其他地方,根据用户是否通过身份验证显示一些部分。

你可以通过 props 穿过每个组件,但这有点疯狂。这会将所有中间组件与他们绝对不关心的数据联系起来。

因此,在这种情况下,上下文完全有意义。您可以setContextApp组件中,并且可以仅从需要它的组件访问它。

另一个示例是某种“复合”组件,其中您有一个包装组件(例如表单)和可以在其中使用的多个组件(例如输入),这取决于容器中的某些设置。

<Form>
  <Input />
</Form>
Run Code Online (Sandbox Code Playgroud)

在这里,Form组件不能将 props 传递给Input组件,因为Input不是直接在Form组件中创建的。它是通过一个槽的方式喂给它的,而Form这个槽的内容是看不到的。

仍然Input嵌套Form在生成的组件树下,因此您可以通过上下文在它们之间传递数据。

总而言之, context 真正适用于您不能使用 props 的情况。要么因为它不切实际并导致糟糕的架构,要么因为它在技术上是不可能的(插槽)。

作为上下文的替代方案,您可以将数据存储在提供者和使用者都可以访问的专用 JS 模块中(例如import { setData, getData } from './data-source.js'),但会使您的组件成为单例。这些数据只能是全局的。另一方面,使用上下文,您可以根据需要拥有尽可能多的独立数据“范围”,每个数据提供程序组件实例都有一个。在Form上面的示例中,多个<Form>组件可以同时共存于您的应用程序中,每个组件在上下文中都有自己的数据。(它们甚至可以相互嵌套并且可以工作。)

总而言之,这里有一条建议。Svelte 中的上下文是使用 JSMap对象实现的,因此您不必使用原始字符串作为上下文键。我通常使用从constants.js模块之类的东西中导出的普通对象(或者如果你想变得花哨的话,也可以使用 Symbol)。这在很大程度上减轻了我之前提到的错误输入和 IDE 混淆问题。

constants.js

export const key = {name: 'my-context'}
Run Code Online (Sandbox Code Playgroud)

Form.svelte

<script>
  import { setContext } from 'svelte'
  import { key } from './constants.js'

  setContext(key, { ... })
</script>

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

Input.svelte

<script>
  import { getContext } from 'svelte'
  import { key } from './constants.js'

  const { ... } = getContext(key)
</script>

...
Run Code Online (Sandbox Code Playgroud)

这消除了与原始字符串可能发生的上下文键冲突的任何风险。它将错误输入变成了快速失败和嘈杂的崩溃错误(这很好)。它为您的 IDE 提供了关于代码中发生了什么的更好的线索(开发工具可以轻松解析 ES 导入,而字符串对它们来说只是随机的 blob),当您使用它时,它对您更有帮助将需要重构...