Konfigurasi aplikasi pada Angular. Praktik terbaik

Bagaimana mengelola file dan tujuan konfigurasi lingkungan


Saat Anda membuat aplikasi bersudut menggunakan alat Angular CLI atau Nrwl Nx , Anda selalu memiliki folder dengan file konfigurasi lingkungan:


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

Anda dapat mengubah nama environment.prod.ts menjadi environment.production.ts misalnya, Anda juga dapat membuat file konfigurasi tambahan seperti environment.qa.ts atau environment.staging.ts .


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

File environment.ts digunakan secara default. Untuk menggunakan file yang tersisa, Anda harus membuka angular.json dan mengkonfigurasi bagian fileReplacements dalam konfigurasi build dan menambahkan blok ke konfigurasi serve dan e2e .


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

Untuk membangun atau menjalankan aplikasi dengan lingkungan tertentu, gunakan perintah:


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

Buat antarmuka untuk file lingkungan


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

Jangan gunakan file lingkungan secara langsung, hanya melalui DI


Menggunakan variabel global dan impor langsung melanggar pendekatan OOP dan memperumit testabilitas kelas Anda. Karena itu, lebih baik untuk membuat layanan yang dapat disuntikkan ke komponen Anda dan layanan lainnya. Berikut adalah contoh layanan seperti itu dengan kemampuan untuk menentukan nilai default.


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

Pisahkan konfigurasi lingkungan dan logika bisnis Anda


Konfigurasi lingkungan hanya mencakup properti yang berhubungan dengan lingkungan, misalnya apiUrl . Idealnya, konfigurasi lingkungan harus terdiri dari dua properti:


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

Juga dalam konfigurasi ini Anda dapat menambahkan properti untuk mengaktifkan debugMode: mode debug benar atau Anda dapat menambahkan nama server di mana environmentName: aplikasi 'QA' berjalan, tetapi jangan lupa bahwa ini adalah praktik yang sangat buruk jika kode Anda mengetahui sesuatu tentang server yang menjalankannya .


Jangan pernah menyimpan informasi sensitif atau kata sandi dalam konfigurasi lingkungan.


Pengaturan konfigurasi lain seperti maxItemsOnPage atau galleryAnimationSpeed harus disimpan di tempat lain dan disarankan untuk menggunakan configuration.service.ts, yang dapat menerima pengaturan dari beberapa titik akhir atau cukup memuat config.json dari folder aset.


1. Pendekatan asinkron (digunakan ketika konfigurasi dapat berubah dalam runtime)


 // 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. Pendekatan sinkron (digunakan saat konfigurasi hampir tidak pernah berubah)


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

Ganti variabel lingkungan selama penerapan atau runtime


Banyak tim melanggar aturan "Bangun sekali, gunakan banyak" dengan merakit aplikasi untuk setiap lingkungan alih-alih hanya mengubah konfigurasi dalam bangunan yang sudah dibangun.


Jangan membuat rakitan terpisah dengan konfigurasi yang berbeda, sebagai gantinya, gunakan hanya satu rakitan produksi dan nilai-nilai pengganti selama penyebaran atau selama eksekusi kode. Ada beberapa opsi untuk melakukan ini:


Ganti nilai dengan placeholder di file lingkungan yang akan diganti dalam perakitan akhir selama penyebaran


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

Selama penyebaran, string APPLICATION_API_URL harus diganti dengan alamat asli dari server api.


Gunakan variabel global dan menyuntikkan file konfigurasi dengan volume docker


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

Terima kasih atas perhatian Anda pada artikel ini, saya akan dengan senang hati memberikan kritik dan komentar yang membangun.




Bergabung juga dengan komunitas kami di Medium , Telegram atau Twitter .

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


All Articles