如何使Angular模拟服务可摇树 [英] How to make Angular mock service tree-shakeable

查看:89
本文介绍了如何使Angular模拟服务可摇树的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

上下文


在Angular 9项目中,我正在使用两种环境:生产和& 模拟


在核心模块中,我检查模拟环境。



  • 如果使用模拟配置进行构建,则我将注入模拟服务,这些服务将返回模拟数据,因此不会进行外部http请求。



  • 如果进行了构建




我这样做是这样的:


core.module.ts


  @NgModule({
声明:[],
提供者:[],
导入:[BrowserModule,HttpClientModule],
出口:[],
})
出口类CoreModule {}


country.service.proxy.ts


  const countryServiceFactory =(
_http:HttpClient,
_errorUtil:ErrorUtilService
)=> {
return isMock
吗?新的ServiceMock()
:新的服务(_http,_errorUtil);
};

@Injectable({
提供了In:CoreModule,
useFactory:countryServiceFactory,
})
导出抽象类CountryServiceProxy {
abstract getCountries( ):Observable< CountryWithLanguages []> ;;
}

其中 ServiceMock 服务实现相同的接口。


这可行。


问题


代码不可可摇树。结果是,在我的捆绑包中(当我运行 ng build --prod 时)甚至包括了模拟服务。


我想要在开发过程中将每个服务从模拟切换到生产。


目标


我如何使Angular仅捆绑将要捆绑的服务




我正在使用:

  Angular CLI:9.0。 4 
节点:13.6.0
操作系统:darwin x64

常春藤工作区:是

谢谢! :)

解决方案

我刚刚尝试了一种似乎可行的方法:




  • 在您的环境中声明相关的服务工厂。{env} .ts 文件

  • 使用环境工厂作为您的服务提供商



我的测试设置:



基类

  @Injectable()
导出抽象类TestService {
抽象环境:字符串;
}

开发服务

  @Injectable()
导出类DevTestService扩展了TestService {
environment ='qwertydev';
}

产品服务

  @Injectable()
出口类ProdTestService扩展了TestService {
environment ='qwertyprod';
}

environment.ts

 导出const环境= {
testServiceFactory:()=>新的DevTestService()
};

environment.production.ts

  export const environment = {
testServiceFactory:()=>新的ProdTestService()
};

app.module.ts

 提供者:[
{提供:TestService,useFactory:environment.testServiceFactory}
],

app.component.ts



< pre class = lang-js prettyprint-override> constructor(private testService:TestService){}

ngOnInit(){
console.log(this.testService 。得到());
}

当我检查构建文件时,仅找到在开发版本中使用qwertydev ,在生产版本中使用 qwertprod ,这表明它们已经摇晃了。



我使用字符串 qwerty * 使缩小后的构建文件搜索变得容易。



在模块中声明服务



我已经在模块中声明了提供程序,以避免循环引用。通过将服务声明为 providedIn:Module ,很容易引入循环引用。



您可以解决此问题通过声明第三方模块,但这似乎有些矫kill过正。



我已经在一个较旧的答案中证明了这一点: @ Injectable()装饰器和提供程序数组



替代方法



在环境文件中声明服务工厂并不完全正确。我只是为了简单而进行测试。您可以创建自己的一组特定于环境的文件,这些文件在构建时会以与环境文件相同的方式被覆盖,但是坦率地说,这听起来像是一场噩梦。


Context

In an Angular 9 project, I am working with two environments: production & mock.

In the Core Module, I check for mock environment.

  • If build is made with mock configuration I inject mocked services that return mocked data, so no external http requests are made.

  • If build is made with prod configuration, real services are injected.

I do it like this:

 core.module.ts

@NgModule({
  declarations: [],
  providers: [],
  imports: [BrowserModule, HttpClientModule],
  exports: [],
})
export class CoreModule {}

country.service.proxy.ts

const countryServiceFactory = (
  _http: HttpClient,
  _errorUtil: ErrorUtilService
) => {
  return isMock
    ? new ServiceMock()
    : new Service(_http, _errorUtil);
};

@Injectable({
  providedIn: CoreModule,
  useFactory: countryServiceFactory,
})
export abstract class CountryServiceProxy {
  abstract getCountries(): Observable<CountryWithLanguages[]>;
}

Where ServiceMock and Service implement the same interface.

This works.

Problem

Code is not tree shakeable. The result is that in my bundle (when I run ng build --prod) even the mock services are included.

I want to switch each service from mock to prod during development.

Goal

How can I make Angular to bundle only the service that it is going to be used?


I am using:

Angular CLI: 9.0.4
Node: 13.6.0
OS: darwin x64

Ivy Workspace: Yes

Thank you! :)

解决方案

I have just tried one approach that seems to work:

  • Declare the relevant service factory in your environment.{env}.ts files
  • Use the environment factory as your service provider

My test setup:

base class

@Injectable()
export abstract class TestService {
  abstract environment: string;
}

dev service

@Injectable()
export class DevTestService extends TestService {
  environment = 'qwertydev';
}

prod service

@Injectable()
export class ProdTestService extends TestService {
  environment = 'qwertyprod';
}

environment.ts

export const environment = {
  testServiceFactory: () => new DevTestService()
};

environment.production.ts

export const environment = {
  testServiceFactory: () => new ProdTestService()
};

app.module.ts

providers: [
  { provide: TestService, useFactory: environment.testServiceFactory }
],

app.component.ts

constructor(private testService: TestService) {}

ngOnInit() {
  console.log(this.testService.get());
}

When I inspect my build files, I only find qwertydev in the dev build, and qwertprod in the prod build, which suggests that they have been tree-shaken.

I used the strings qwerty* to make it easy to search the build files after minification.

Declaring services in the module

I have declared the provider in the module to avoid circular references. It is easy to introduce a circular reference by declaring a service as providedIn: Module.

You can work around this by declaring a third-party module, but this seems overkill.

I have demonstrated this in an older answer: @Injectable() decorator and providers array

Alternative approaches

It doesn't quite feel right declaring service factories in the environment files. I did it for testing just for simplicity. You could create you own set of environment-specific files that are overwritten at build time in the same way as the environment files, but quite frankly this sounds like a maintenance nightmare.

这篇关于如何使Angular模拟服务可摇树的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆