Anwendungskonfiguration unter Angular. Best Practices

So verwalten Sie Umgebungskonfigurationsdateien und -ziele


Wenn Sie eine Winkelanwendung mit den Tools Angular CLI oder Nrwl Nx erstellt haben , haben Sie immer einen Ordner mit Umgebungskonfigurationsdateien:


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

Sie können environment.prod.ts in environment.production.ts umbenennen. Sie können beispielsweise auch zusätzliche Konfigurationsdateien wie environment.qa.ts oder environment.staging.ts erstellen.


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

Die Datei environment.ts wird standardmäßig verwendet. Um die verbleibenden Dateien zu verwenden, müssen Sie angle.json öffnen und den Abschnitt fileReplacements in der Build- Konfiguration konfigurieren und Blöcke zu den Serve- und E2E- Konfigurationen hinzufügen .


 { "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" } } } } } 

Verwenden Sie die folgenden Befehle, um eine Anwendung mit einer bestimmten Umgebung zu erstellen oder auszuführen:


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

Erstellen Sie eine Schnittstelle für Umgebungsdateien


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

Verwenden Sie Umgebungsdateien nicht direkt, sondern nur über DI


Die Verwendung globaler Variablen und direkter Importe verstößt gegen den OOP-Ansatz und erschwert die Testbarkeit Ihrer Klassen. Daher ist es besser, einen Dienst zu erstellen, der in Ihre Komponenten und andere Dienste eingefügt werden kann. Hier ist ein Beispiel für einen solchen Service mit der Möglichkeit, einen Standardwert anzugeben.


 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 { } 

Trennen Sie Ihre Umgebungskonfiguration und Geschäftslogik


Die Umgebungskonfiguration enthält nur Eigenschaften, die sich auf die Umgebung beziehen, z. B. apiUrl . Idealerweise sollte die Umgebungskonfiguration aus zwei Eigenschaften bestehen:


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

Auch in dieser Konfiguration können Sie eine Eigenschaft hinzufügen, um debugMode: true mode debug zu aktivieren, oder Sie können den Namen des Servers hinzufügen, auf dem der Anwendungsumgebungsname : 'QA' ausgeführt wird. Vergessen Sie jedoch nicht, dass dies eine sehr schlechte Vorgehensweise ist, wenn Ihr Code etwas über den Server weiß, auf dem er ausgeführt wird .


Speichern Sie niemals vertrauliche Informationen oder Kennwörter in einer Umgebungskonfiguration.


Andere Konfigurationseinstellungen wie maxItemsOnPage oder galleryAnimationSpeed sollten an einem anderen Ort gespeichert werden. Es wird empfohlen, configuration.service.ts zu verwenden , um Einstellungen von einem Endpunkt zu erhalten oder config.json einfach aus dem Assets-Ordner zu laden.


1. Asynchroner Ansatz (wird verwendet, wenn sich die Konfiguration zur Laufzeit ändern kann)


 // 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. Synchroner Ansatz (wird verwendet, wenn sich die Konfiguration fast nie ändert)


 // 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); } } 

Ersetzen Sie Umgebungsvariablen während der Bereitstellung oder Laufzeit


Viele Teams verstoßen gegen die Regel "Einmal erstellen, viele bereitstellen", indem sie die Anwendung für jede Umgebung zusammenstellen, anstatt nur die Konfiguration im bereits erstellten Build zu ändern.


Erstellen Sie keine separaten Assemblys mit unterschiedlichen Konfigurationen, sondern verwenden Sie während der Bereitstellung oder während der Codeausführung nur eine Produktionsassembly und Ersatzwerte. Hierfür gibt es mehrere Möglichkeiten:


Ersetzen Sie die Werte durch Platzhalter in den Umgebungsdateien, die in der Endmontage während der Bereitstellung ersetzt werden


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

Während der Bereitstellung sollte die Zeichenfolge APPLICATION_API_URL durch die tatsächliche Adresse des API-Servers ersetzt werden.


Verwenden Sie globale Variablen und injizieren Sie Konfigurationsdateien mit Docker-Volumes


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

Vielen Dank für Ihre Aufmerksamkeit auf den Artikel, ich werde mich über konstruktive Kritik und Kommentare freuen.




Treten Sie auch unserer Community auf Medium , Telegram oder Twitter bei .

Source: https://habr.com/ru/post/de477214/


All Articles