如何使Angular模拟服务可摇树 [英] How to make Angular mock service tree-shakeable
问题描述
上下文
在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屋!