Javascript Svelte-在常规道具上使用上下文API(setContext/getContext)

Javascript Svelte-在常规道具上使用上下文API(setContext/getContext),javascript,svelte,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} {say

下面是一个简单的例子:

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

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

<Button {text} {sayHello}/>
<Button {text} {sayHello}/>
<Button {text} {sayHello}/>

从“./Button.svelte”导入按钮;
让文本='单击我!';
让我们说你好=()=>警惕('Hello!');
如果我没弄错的话,因为可能会有很多
,所以最好忽略一些道具

下面是上下文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/>

从“./Button.svelte”导入按钮;
从“svelte”导入{setContext};
从'/data.js'导入{text,sayHello};
setContext(文本“单击我!”);
setContext(sayHello,()=>alert('Hello!'));
/Button.svelte
的某个地方有
getContext()
用法等


那么,省略类似道具的能力是使用Svelte的上下文API的唯一原因吗

那么,省略类似道具的能力是使用Svelte上下文API的唯一原因吗

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

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

通过道具,可以明确按钮需要什么数据以及数据来自何处。另一方面,在上下文中,您一次只能看到关系的一面。在父级中,您看不到数据是如何使用的(或者即使它仍然被使用)。在孩子身上也是一样,你看不出它来自哪里

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

因此,当你在编写代码的过程中,把所有的东西都记在脑子里时,节省一点打字似乎是一个好主意,但它实际上增加了代码的复杂性,可能会捉弄你,让你以后头疼不已。。。如果你想得到我的意见,这不是一个好的交易

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

例如,您的应用程序中可能有某种用户会话。它很可能存储在组件树(比如,App)根附近的组件中,但在嵌套更深的几个级别的组件中需要它。例如,在显示用户名的组件中。或者页面中的其他位置,根据用户是否经过身份验证来显示某些部分

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

所以,在这种情况下,上下文是完全有意义的。您可以在
App
组件中
setContext
,并可以仅从需要它的组件访问它

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


这里,
表单
组件无法将道具传递给
输入
组件,因为
输入
不是直接在
表单
组件中创建的。它是通过一个插槽馈送给它的,
表单
看不到此插槽的内容

但是,
Input
嵌套在结果组件树的
Form
下,因此您可以通过上下文在它们之间传递数据

总而言之,上下文实际上是指不能使用道具的情况。要么因为它不可行,导致糟糕的体系结构,要么因为它在技术上是不可能的(插槽)

作为上下文的替代方案,您可以将数据存储在提供者和使用者都可以访问的专用JS模块中(例如,
import{setData,getData}from./data source.JS'
),但这将使您的组件成为单例组件。这些数据只能是全球性的。另一方面,使用上下文,您可以拥有所需的任意多个独立数据“作用域”,一个用于数据提供程序组件的每个实例。在上面的
表单
示例中,多个
组件可以同时共存于您的应用程序中,每个组件在上下文中都有自己的数据。(它们甚至可以嵌套在一起,这样就可以工作了。)

最后,这里有一条建议。Svelte中的上下文是用JS
Map
object实现的,因此您不必使用原始字符串作为上下文键。我通常使用从
constants.js
模块导出的普通对象(如果您喜欢,也可以使用符号)。这在很大程度上缓解了我前面提到的输入错误和IDE混淆问题

constants.js

export const key={name:'my context'}
Form.slvelte


从“svelte”导入{setContext}
从“./constants.js”导入{key}
setContext(键,{…})
Input.svelte


从“svelte”导入{getContext}
从“./constants.js”导入{key}
常量{…}=getContext(键)
...

这消除了可能与原始字符串发生上下文键冲突的任何风险。它会将错误输入变回一个快速失败并大声崩溃的错误(这是好的)。它还为IDE提供了一个更好的线索来了解代码中发生了什么(ES导入可以很容易地被开发工具解析,而字符串对它们来说只是随机的blob),Rixo:我感谢你的详细回答,因为我很难理解如何组织单击的按钮组件并向其传递道具。你