如何使 Angular 模拟服务 tree-shakeable
How to make Angular mock service tree-shakeable
上下文
在一个 Angular 9 项目中,我在两个环境中工作:production & mock.
在核心模块中,我检查模拟环境。
如果构建是使用模拟配置进行的,我会注入 return 模拟数据的模拟服务,因此不会发出外部 http 请求。
如果使用 prod 配置进行构建,则会注入真正的服务。
我是这样做的:
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[]>;
}
其中 ServiceMock
和 Service
实现相同的接口。
这有效。
问题
代码不是 tree shakeable。结果是在我的捆绑包中(当我 运行 ng build --prod
时)甚至包含模拟服务。
我想在开发过程中将每个服务从 mock 切换到 prod。
目标
如何使 Angular 仅捆绑将要使用的服务?
我正在使用:
Angular CLI: 9.0.4
Node: 13.6.0
OS: darwin x64
Ivy Workspace: Yes
谢谢! :)
我刚刚尝试了一种似乎有效的方法:
- 在您的
environment.{env}.ts
文件中声明相关服务工厂
- 使用环境工厂作为您的服务提供者
我的测试设置:
基础class
@Injectable()
export abstract class TestService {
abstract environment: string;
}
开发服务
@Injectable()
export class DevTestService extends TestService {
environment = 'qwertydev';
}
产品服务
@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());
}
当我检查我的构建文件时,我只在开发构建中找到 qwertydev
,在生产构建中找到 qwertprod
,这表明它们已经过 tree-shaken。
我使用字符串 qwerty*
以便于在缩小后搜索构建文件。
在模块中声明服务
我已经在模块中声明了提供者以避免循环引用。通过将服务声明为 providedIn: Module
.
很容易引入循环引用
您可以通过声明第三方模块来解决这个问题,但这似乎有点过分了。
我已经在一个较早的答案中证明了这一点:
替代方法
相当感觉在环境文件中声明服务工厂不对。为了简单起见,我这样做是为了测试。您可以创建自己的一组特定于环境的文件,这些文件在构建时以与环境文件相同的方式被覆盖,但坦率地说,这听起来像是一场维护噩梦。
上下文
在一个 Angular 9 项目中,我在两个环境中工作:production & mock.
在核心模块中,我检查模拟环境。
如果构建是使用模拟配置进行的,我会注入 return 模拟数据的模拟服务,因此不会发出外部 http 请求。
如果使用 prod 配置进行构建,则会注入真正的服务。
我是这样做的:
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[]>;
}
其中 ServiceMock
和 Service
实现相同的接口。
这有效。
问题
代码不是 tree shakeable。结果是在我的捆绑包中(当我 运行 ng build --prod
时)甚至包含模拟服务。
我想在开发过程中将每个服务从 mock 切换到 prod。
目标
如何使 Angular 仅捆绑将要使用的服务?
我正在使用:
Angular CLI: 9.0.4
Node: 13.6.0
OS: darwin x64
Ivy Workspace: Yes
谢谢! :)
我刚刚尝试了一种似乎有效的方法:
- 在您的
environment.{env}.ts
文件中声明相关服务工厂 - 使用环境工厂作为您的服务提供者
我的测试设置:
基础class
@Injectable()
export abstract class TestService {
abstract environment: string;
}
开发服务
@Injectable()
export class DevTestService extends TestService {
environment = 'qwertydev';
}
产品服务
@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());
}
当我检查我的构建文件时,我只在开发构建中找到 qwertydev
,在生产构建中找到 qwertprod
,这表明它们已经过 tree-shaken。
我使用字符串 qwerty*
以便于在缩小后搜索构建文件。
在模块中声明服务
我已经在模块中声明了提供者以避免循环引用。通过将服务声明为 providedIn: Module
.
您可以通过声明第三方模块来解决这个问题,但这似乎有点过分了。
我已经在一个较早的答案中证明了这一点:
替代方法
相当感觉在环境文件中声明服务工厂不对。为了简单起见,我这样做是为了测试。您可以创建自己的一组特定于环境的文件,这些文件在构建时以与环境文件相同的方式被覆盖,但坦率地说,这听起来像是一场维护噩梦。