Chúng tôi đã gặp sự cố này cách đây nhiều năm trước khi tôi tham gia và đưa ra giải pháp sử dụng bộ nhớ cục bộ cho thông tin người dùng và môi trường. Angular chính xác là 1,0 ngày. Trước đây, chúng tôi đã tạo động một tệp js trong thời gian chạy, sau đó sẽ đặt các url api đã tạo vào một biến toàn cục. Ngày nay, chúng ta đang sử dụng OOP nhiều hơn một chút và không sử dụng bộ nhớ cục bộ cho bất kỳ thứ gì.
Tôi đã tạo một giải pháp tốt hơn cho cả việc xác định môi trường và tạo url api.
Điều này khác nhau như thế nào?
Ứng dụng sẽ không tải trừ khi tệp config.json được tải. Nó sử dụng các chức năng của nhà máy để tạo ra mức độ SOC cao hơn. Tôi có thể đóng gói điều này vào một dịch vụ, nhưng tôi chưa bao giờ thấy lý do gì khi điểm giống nhau duy nhất giữa các phần khác nhau của tệp là chúng tồn tại cùng nhau trong tệp. Có một chức năng gốc cho phép tôi chuyển trực tiếp chức năng vào một mô-đun nếu nó có khả năng chấp nhận một chức năng. Cuối cùng, tôi có thời gian dễ dàng hơn khi thiết lập InjectionTokens khi các chức năng của nhà máy có sẵn để sử dụng.
Nhược điểm?
Bạn không gặp may khi sử dụng thiết lập này (và hầu hết các câu trả lời khác) nếu mô-đun bạn muốn định cấu hình không cho phép chuyển một hàm gốc vào forRoot () hoặc forChild () và không có cách nào khác để cấu hình gói bằng cách sử dụng một chức năng gốc.
Hướng dẫn
- Sử dụng tìm nạp để truy xuất tệp json, tôi lưu trữ đối tượng trong cửa sổ và đưa ra một sự kiện tùy chỉnh. - nhớ cài đặt whatwg-fetch và thêm nó vào polyfills.ts của bạn để tương thích với IE
- Có người nghe sự kiện lắng nghe sự kiện tùy chỉnh.
- Trình nghe sự kiện nhận sự kiện, truy xuất đối tượng từ cửa sổ để chuyển cho một đối tượng có thể quan sát được và xóa những gì đã được lưu trữ trong cửa sổ.
- Bootstrap Angular
- Đây là lúc giải pháp của tôi bắt đầu thực sự khác biệt -
- Tạo tệp xuất một giao diện có cấu trúc đại diện cho config.json của bạn - nó thực sự giúp ích cho sự nhất quán về kiểu và phần mã tiếp theo yêu cầu một kiểu và không chỉ định
{}
hoặc any
khi bạn biết bạn có thể chỉ định một cái gì đó cụ thể hơn
- Tạo BehaviorSubject mà bạn sẽ chuyển tệp json đã phân tích cú pháp vào trong bước 3.
- Sử dụng các chức năng của nhà máy để tham chiếu các phần khác nhau trong cấu hình của bạn để duy trì SOC
- Tạo InjectionTokens cho các nhà cung cấp cần kết quả của các chức năng xuất xưởng của bạn
- và / hoặc -
- Chuyển trực tiếp các hàm của nhà máy vào các mô-đun có khả năng chấp nhận một hàm trong các phương thức forRoot () hoặc forChild () của nó.
- main.ts
Tôi kiểm tra window ["environment"] không được điền trước khi tạo trình xử lý sự kiện để cho phép khả năng giải pháp trong đó window ["environment"] được điền bằng một số phương tiện khác trước khi mã trong main.ts thực thi.
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { configurationSubject } from './app/utils/environment-resolver';
var configurationLoadedEvent = document.createEvent('Event');
configurationLoadedEvent.initEvent('config-set', true, true);
fetch("../../assets/config.json")
.then(result => { return result.json(); })
.then(data => {
window["environment"] = data;
document.dispatchEvent(configurationLoadedEvent);
}, error => window.location.reload());
if(!window["environment"]) {
document.addEventListener('config-set', function(e){
if (window["environment"].production) {
enableProdMode();
}
configurationSubject.next(window["environment"]);
window["environment"] = undefined;
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.log(err));
});
}
--- môi trường phân giải.ts
Tôi chỉ định một giá trị cho BehaviorSubject bằng cách sử dụng window ["environment"] để dự phòng. Bạn có thể nghĩ ra một giải pháp trong đó cấu hình của bạn đã được tải trước và cửa sổ ["environment"] đã được điền vào thời điểm bất kỳ mã ứng dụng Angular nào của bạn được chạy, bao gồm cả mã trong main.ts
import { BehaviorSubject } from "rxjs";
import { IConfig } from "../config.interface";
const config = <IConfig>Object.assign({}, window["environment"]);
export const configurationSubject = new BehaviorSubject<IConfig>(config);
export function resolveEnvironment() {
const env = configurationSubject.getValue().environment;
let resolvedEnvironment = "";
switch (env) {
// case statements for determining whether this is dev, test, stage, or prod
}
return resolvedEnvironment;
}
export function resolveNgxLoggerConfig() {
return configurationSubject.getValue().logging;
}
- app.module.ts - Đã lược bớt để dễ hiểu hơn
Sự thật thú vị! Các phiên bản cũ hơn của NGXLogger yêu cầu bạn chuyển một đối tượng vào LoggerModule.forRoot (). Trên thực tế, LoggerModule vẫn hoạt động! NGXLogger vui lòng tiết lộ LoggerConfig mà bạn có thể ghi đè cho phép bạn sử dụng một chức năng gốc để thiết lập.
import { resolveEnvironment, resolveNgxLoggerConfig, resolveSomethingElse } from './environment-resolvers';
import { LoggerConfig } from 'ngx-logger';
@NgModule({
modules: [
SomeModule.forRoot(resolveSomethingElse)
],
providers:[
{
provide: ENVIRONMENT,
useFactory: resolveEnvironment
},
{
provide: LoggerConfig,
useFactory: resolveNgxLoggerConfig
}
]
})
export class AppModule
Phụ lục
Tôi đã giải quyết việc tạo url API của mình như thế nào?
Tôi muốn có thể hiểu từng url đã làm gì thông qua một nhận xét và muốn đánh máy vì đó là điểm mạnh nhất của TypeScript so với javascript (IMO). Tôi cũng muốn tạo trải nghiệm cho các nhà phát triển khác để thêm các điểm cuối mới và các apis càng liền mạch càng tốt.
Tôi đã tạo một lớp sử dụng môi trường (dev, test, stage, prod, "" và v.v.) và chuyển giá trị này cho một loạt các lớp [1-N] có công việc là tạo url cơ sở cho mỗi bộ sưu tập API . Mỗi ApiCollection chịu trách nhiệm tạo url cơ sở cho mỗi bộ sưu tập API. Có thể là API của riêng chúng tôi, API của nhà cung cấp hoặc thậm chí là liên kết bên ngoài. Lớp đó sẽ chuyển url cơ sở đã tạo vào mỗi api tiếp theo mà nó chứa. Đọc đoạn mã dưới đây để xem ví dụ về xương trần. Sau khi thiết lập, rất đơn giản để một nhà phát triển khác thêm điểm cuối khác vào lớp Api mà không cần phải chạm vào bất kỳ thứ gì khác.
TLDR; các nguyên tắc cơ bản của OOP và lười biếng để tối ưu hóa bộ nhớ
@Injectable({
providedIn: 'root'
})
export class ApiConfig {
public apis: Apis;
constructor(@Inject(ENVIRONMENT) private environment: string) {
this.apis = new Apis(environment);
}
}
export class Apis {
readonly microservices: MicroserviceApiCollection;
constructor(environment: string) {
this.microservices = new MicroserviceApiCollection(environment);
}
}
export abstract class ApiCollection {
protected domain: any;
constructor(environment: string) {
const domain = this.resolveDomain(environment);
Object.defineProperty(ApiCollection.prototype, 'domain', {
get() {
Object.defineProperty(this, 'domain', { value: domain });
return this.domain;
},
configurable: true
});
}
}
export class MicroserviceApiCollection extends ApiCollection {
public member: MemberApi;
constructor(environment) {
super(environment);
this.member = new MemberApi(this.domain);
}
resolveDomain(environment: string): string {
return `https://subdomain${environment}.actualdomain.com/`;
}
}
export class Api {
readonly base: any;
constructor(baseUrl: string) {
Object.defineProperty(this, 'base', {
get() {
Object.defineProperty(this, 'base',
{ value: baseUrl, configurable: true});
return this.base;
},
enumerable: false,
configurable: true
});
}
attachProperty(name: string, value: any, enumerable?: boolean) {
Object.defineProperty(this, name,
{ value, writable: false, configurable: true, enumerable: enumerable || true });
}
}
export class MemberApi extends Api {
get MemberInfo() {
this.attachProperty("MemberInfo", `${this.base}basic-info`);
return this.MemberInfo;
}
constructor(baseUrl: string) {
super(baseUrl + "member/api/");
}
}