Angular上的应用程序配置。 最佳实务

如何管理环境配置文件和目标


使用Angular CLINrwl Nx工具创建Angle应用程序时,始终会有一个包含环境配置文件的文件夹:


<APP_FOLDER>/src/environments/ └──environment.ts └──environment.prod.ts 

例如,您可以将environment.prod.ts重命名为environment.production.ts ,也可以创建其他配置文件,例如environment.qa.tsenvironment.staging.ts


 <APP_FOLDER>/src/environments/ └──environment.ts └──environment.prod.ts └──environment.qa.ts └──environment.staging.ts 

默认情况下使用environment.ts文件。 要使用其余文件,您必须打开angular.json并在构建配置中配置fileReplacements部分,并将块添加到servee2e配置。


 { "architect":{ "build":{ "configurations":{ "production":{ "fileReplacements":[ { "replace":"<APP_FOLDER>/src/environments/environment.ts", "with":"<APP_FOLDER>/src/environments/environment.production.ts" } ] }, "staging":{ "fileReplacements":[ { "replace":"<APP_FOLDER>/src/environments/environment.ts", "with":"<APP_FOLDER>/src/environments/environment.staging.ts" } ] } } }, "serve":{ "configurations":{ "production":{ "browserTarget":"app-name:build:production" }, "staging":{ "browserTarget":"app-name:build:staging" } } }, "e2e":{ "configurations":{ "production":{ "browserTarget":"app-name:serve:production" }, "staging":{ "browserTarget":"app-name:serve:staging" } } } } } 

要在特定环境下构建或运行应用程序,请使用以下命令:


 ng build --configuration=staging ng start --configuration=staging ng e2e --configuration=staging  ng build --prod     ng build --configuration=production 

创建环境文件的界面


 // environment-interface.ts export interface EnvironmentInterface { production: boolean; apiUrl: string; } // environment.ts export const environment: EnvironmentInterface = { production: false, apiUrl: 'https://api.url', }; 

不要仅通过DI直接使用环境文件


使用全局变量和直接导入违反了OOP方法,并使类的可测试性复杂化。 因此,最好创建一个可以注入到组件和其他服务中的服务。 这是具有指定默认值功能的此类服务的示例。


 export const ENVIRONMENT = new InjectionToken<{ [key: string]: any }>('environment'); @Injectable({ providedIn: 'root', }) export class EnvironmentService { private readonly environment: any; // We need @Optional to be able start app without providing environment file constructor(@Optional() @Inject(ENVIRONMENT) environment: any) { this.environment = environment !== null ? environment : {}; } getValue(key: string, defaultValue?: any): any { return this.environment[key] || defaultValue; } } @NgModule({ imports: [ BrowserModule, HttpClientModule, AppRoutingModule, ], declarations: [ AppComponent, ], // We declare environment as provider to be able to easy test our service providers: [{ provide: ENVIRONMENT, useValue: environment }], bootstrap: [AppComponent], }) export class AppModule { } 

分离环境配置和业务逻辑


环境配置仅包含与环境相关的属性,例如apiUrl 。 理想情况下,环境配置应包含两个属性:


 export const environment = { production: true, apiUrl: 'https://api.url', }; 

同样在此配置中,您可以添加属性以启用debugMode:true模式调试 ,也可以添加运行应用程序environmentName:'QA'的服务器的名称,但是请不要忘记,如果您的代码对正在运行的服务器有所了解,这是非常不好的做法。


切勿在环境配置中存储任何敏感信息或密码。


其他配置设置(例如maxItemsOnPagegalleryAnimationSpeed)应存储在其他位置,建议使用configuration.service.ts,它可以从某个端点接收设置,或者只是从资产文件夹中加载config.json。


1.异步方法(在运行时配置可能更改时使用)


 // assets/config.json { "galleryAnimationSpeed": 5000 } // configuration.service.ts // ------------------------------------------------------ @Injectable({ providedIn: 'root', }) export class ConfigurationService { private configurationSubject = new ReplaySubject<any>(1); constructor(private httpClient: HttpClient) { this.load(); } // method can be used to refresh configuration load(): void { this.httpClient.get('/assets/config.json') .pipe( catchError(() => of(null)), filter(Boolean), ) .subscribe((configuration: any) => this.configurationSubject.next(configuration)); } getValue(key: string, defaultValue?: any): Observable<any> { return this.configurationSubject .pipe( map((configuration: any) => configuration[key] || defaultValue), ); } } // app.component.ts // ------------------------------------------------------ @Component({ selector: 'app-root', changeDetection: ChangeDetectionStrategy.OnPush, templateUrl: './app.component.html', styleUrls: ['./app.component.scss'] }) export class AppComponent { galleryAnimationSpeed$: Observable<number>; constructor(private configurationService: ConfigurationService) { this.galleryAnimationSpeed$ = this.configurationService.getValue('galleryAnimationSpeed', 3000); interval(10000).subscribe(() => this.configurationService.load()); } } 

2.同步方法(在配置几乎从未更改时使用)


 // assets/config.json { "galleryAnimationSpeed": 5000 } // configuration.service.ts // ------------------------------------------------------ @Injectable({ providedIn: 'root', }) export class ConfigurationService { private configuration = {}; constructor(private httpClient: HttpClient) { } load(): Observable<void> { return this.httpClient.get('/assets/config.json') .pipe( tap((configuration: any) => this.configuration = configuration), mapTo(undefined), ); } getValue(key: string, defaultValue?: any): any { return this.configuration[key] || defaultValue; } } // app.module.ts // ------------------------------------------------------ export function initApp(configurationService: ConfigurationService) { return () => configurationService.load().toPromise(); } @NgModule({ imports: [ BrowserModule, HttpClientModule, AppRoutingModule, ], declarations: [ AppComponent, ], providers: [ { provide: APP_INITIALIZER, useFactory: initApp, multi: true, deps: [ConfigurationService] } ], bootstrap: [AppComponent], }) export class AppModule { } // app.component.ts // ------------------------------------------------------ @Component({ selector: 'app-root', changeDetection: ChangeDetectionStrategy.OnPush, templateUrl: './app.component.html', styleUrls: ['./app.component.scss'] }) export class AppComponent { galleryAnimationSpeed: number; constructor(private configurationService: ConfigurationService) { this.galleryAnimationSpeed = this.configurationService.getValue('galleryAnimationSpeed', 3000); } } 

在部署或运行​​时替换环境变量


许多团队通过为每个环境组装应用程序而不仅仅是在已经构建的构建中更改配置,从而违反了“一次构建,部署很多”的规则。


不要创建具有不同配置的单独程序集;而是在部署或代码执行过程中仅使用一个生产程序集并替换值。 有几种方法可以执行此操作:


将值替换为环境文件中的占位符,在部署期间将在最终程序集中替换这些占位符


 export const environment = { production: true, apiUrl: 'APPLICATION_API_URL', }; 

在部署期间,应将字符串APPLICATION_API_URL替换为api服务器的真实地址。


使用全局变量并将配置文件注入Docker卷


 export const environment = { production: true, apiUrl: window.APPLICATION_API_URL, }; // in index.html before angular app bundles <script src="environment.js"></script> 

感谢您对本文的关注,我将很乐意接受建设性的批评和评论。




也可以通过MediumTelegramTwitter加入我们的社区。

Source: https://habr.com/ru/post/zh-CN477214/


All Articles