如何使用不同的@Injectable进行开发和生产

时间:2017-10-19 03:03:28

标签: javascript angular typescript

我有一个rest-client对象,它在生产环境中将指向一个安静的服务器并与之通信。但在开发模式下,它只是简单地使用浏览器的存储空间来使用。

我认为正确的方法是实现两个接口实现,一个用于生产,一个用于开发。这样,应用程序的其余部分不需要了解生产/开发,并且可以进行相同的测试。我需要做的就是找到一种方法为每个环境注入正确的实例。顺便说一句,我不想​​在生产代码中包含开发版本!

有谁知道怎么做?

[UPDATE]

我使用Angular cli开始我的项目,所以我使用Webpack(我想!)。我不想使用外部模拟服务器,因为我更喜欢将测试/开发环境与外部依赖关系隔离开来。这就是我需要在我的前端项目上工作的本身。此外,即使我的问题集中在休息客户端,但当然这个问题可以推广到任何其他单例注射。

我问这个问题,因为我使用的其他DI框架(即Spring)这样做是框架的一部分。所以我期待在这里看到同样的东西。

此时,我知道我可以利用if (environment.production) { import { RestClientService } from './rest-client.service'; } else { import { MockRestClientService as RestClientService } from './mock-rest-client.service'; } ERROR in ... Duplicate identifier 'RestClientService'. ERROR in ... An import declaration can only be used in a namespace or module. 来检查它是否处于生产模式。但首先,这是一个运行时变量,但我想我正在寻找一个编译时的变量(我不确定)。此外,即使我们有该变量,但以下代码导致编译错误:

  foreach (DataRow row in dtBills.Rows)
            {
                classes.UtilityBill ub = new classes.UtilityBill(row);

                if (ub.ApprovedBy > 0)
                {
                    if (ub.RemainingBalance() > 0) { totalOutstanding += ub.RemainingBalance(); numberOfUnpaidBills++; }
                    if (ub.RemainingBalance() > 0 && ub.IsOverDue()) { numberOfOverdueBills++; }
                }
                else
                {
                    if (ub.ApprovedBy == 0)
                    {
                        awaitingApproval++;
                    }
                    else
                    {
                        rejectedBills++;
                    }
                }
            }

1 个答案:

答案 0 :(得分:2)

使用factory provider

export function restClientFactory(http: HttpClient): RestClientService {
  if (environment.production) {
    return new RestClientService(http);
  } else {
    return new MockRestClientService();
  }
}

@NgModule({
  providers: [
    { provide: RestClientService, useFactory: restClientFactory, deps: [ HttpClient ] },
  ],
})
class AppModule {}

使用条件import语句的方法不起作用,因为只允许在文件的顶层使用静态import语句。它们不能嵌套在if/else语句中。您可以使用动态import()来克服此问题,但这也会导致两个实现都包含在您的包中。

不幸的是,没有技术可能在Spring中进行DI的编译时配置。

运行时检查的主要问题是两个实现都将包含在您的生产包中。您可以依赖tree-shaking(生成版本的一部分)来确保您的模拟客户端不包含在生产包中。

由于environment.production变量永远不会改变,因此您可以预期if/else的一个分支永远不可访问,因此可以通过树摇动过程从捆绑中删除。问题是,做树震动的工具无法理解它,因为变量不是声明为const,而是作为对象的属性。

如果您将export const production = true添加到environment.prod.ts并将export const production = false添加到environment.ts并在if/else语句中使用此变量,则其中一个分支应成功树摇动来自生产包。

请注意,此方法依赖于Angular CLI的实现细节。不过我已经成功地在我的一个项目中使用它超过一年了。