Dependency injection 如何将服务注入到苗条的孙子组件中?

Dependency injection 如何将服务注入到苗条的孙子组件中?,dependency-injection,svelte,Dependency Injection,Svelte,我有几个服务类(带有一些“获取这些参数的数据”和一些“计算这些参数的数据”方法),我想将它们注入到我的Svelte组件层次结构中的几个组件中。目前,我看到以下几种选择,没有一种很有吸引力: 将服务作为道具传递。一些中间组件不需要这些服务,只需将它们传递出去。而且它增加了道具的数量 用商店包装服务。这感觉就像是将反应式存储功能误用到了它本不该用于的东西上。从服务中产生的数据大多是静态的,不是非常被动的 使用顶级组件中的服务,将结果作为道具传递给子组件。这会增加更多的道具,因为我在根组件和子组件之

我有几个服务类(带有一些“获取这些参数的数据”和一些“计算这些参数的数据”方法),我想将它们注入到我的Svelte组件层次结构中的几个组件中。目前,我看到以下几种选择,没有一种很有吸引力:

  • 将服务作为道具传递。一些中间组件不需要这些服务,只需将它们传递出去。而且它增加了道具的数量
  • 用商店包装服务。这感觉就像是将反应式存储功能误用到了它本不该用于的东西上。从服务中产生的数据大多是静态的,不是非常被动的
  • 使用顶级组件中的服务,将结果作为道具传递给子组件。这会增加更多的道具,因为我在根组件和子组件之间有一些“布局”组件。然后,这些布局组件必须传递所有道具

在Vue中,我会编写一个插件,添加所有Vue组件中可用的属性。做这件事最简单的方法是什么?

我同意w@richharris。存储(单独的is文件)可能是最有效的。特别是当您有3+个级别的Dom接收数据时

一个选项是使用es6功能,其中导出的变量可以在声明它们的文件中进行更改,这有助于“服务需要一些我在我的入口点js中进行的设置。这些设置来自浏览器环境,因此我无法“烘焙”配置值”,例如

main.js

services.js


我遇到了同样的问题。当不需要安装时,按照Rich Harris的建议执行并从一个单独的JS文件导出服务非常容易,但我遇到了一个问题:延迟加载。与您的问题不同,但类似之处在于服务无法立即提供

我想用动态导入延迟加载Firebase,因为它相当重。这意味着在
import()
承诺得到解决之前,这些服务无法访问数据库,因此导出它们并不那么简单。我找到了几个解决方案:

如果要保持服务的静态,请使用上下文 您提到的商店似乎有些过分,因为服务是静态的您可以使用上下文进行此操作One catch:
setContext
需要在初始化期间同步调用(即不在
onMount
中)。如果您的配置是同步进行的,那么您应该能够避免额外的层,但以下是我所做的:

在应用程序顶部创建一个额外的组件来实例化服务,并将其作为道具传递到根
应用程序
,然后将其传递到
setContext
。您需要在
App
上面添加额外的组件,以确保服务在上下文中设置之前存在。使用Svelte的
{#wait promise}
语法,您可以确保在传递道具之前进行初始化:

InitServices.svelte


从“/config”导入配置;
从“./services”导入createServices;
从“/App.svelte”导入应用程序;
const services=createServices(配置);
{#等待承诺然后服务}

所以。。。我决定用一个商店来代替上下文,结果它工作得很好。反应存储是测试的一个好处,因为您可以使用模拟服务实例化存储,并在需要时动态更新它

我仍然将服务作为道具传递到我的应用程序顶部,以避免空检查,但除此之外,这将清理很多东西。 服务商店.js

从'svelte/store'导入{writeable};
export const serviceStore=可写(null);
InitServices.svelte (同上)

App.svelte


从“./services store”导入serviceStore;
从“/Child.svelte”导入子对象;
出口服务;
serviceStore.set(服务);
{
mockService={getData:jest.fn().mockResolvedValue('helloitsjoe')};
serviceStore.set(mockService);
});
...
它('显示用户名',异步()=>{
给予(孙子);
const name=await findByText('helloitsjoe');
expect(name.toBeTruthy();
});
它('显示错误',异步()=>{
//覆盖模拟服务
mockService.getData.mockRejectedValue(新错误('oh no!'));
给予(孙子);
const message=wait findByText('oh no!');
expect(message.toBeTruthy();
});

如果它不是被动的,那么您就可以设置并获取上下文;有什么原因不能从一个单独的JavaScript文件中导入它们吗?这些服务需要一些设置,就像我在我的入口点js中所做的那样。设置来自浏览器环境,因此我无法“烘焙”配置值。@chiborg您找到执行分层注入的方法了吗?
import AppUI from './App.html'
import { AppStore }  from './store.js'
import { HttpService } from './http.js'
import { cfg } from './cfg.js'
import { initMain }  from './services.js'

// we're off
initMain( AppUI, AppStore, HttpService, cfg  )
let app, store, http

function initMain( App, Store, HttpService, cfg ) {

    http = new HttpService(cfg)                         
    store = new Store(cfg)                      
    app = new App({ store, target: document.body })     
  }

}

export { initMain, app, store, http }