เรามีปัญหานี้เมื่อหลายปีก่อนก่อนที่ฉันจะเข้าร่วมและได้วางโซลูชันที่ใช้พื้นที่จัดเก็บข้อมูลภายในสำหรับข้อมูลผู้ใช้และสภาพแวดล้อม Angular 1.0 วันที่แน่นอน ก่อนหน้านี้เราเคยสร้างไฟล์ js แบบไดนามิกที่รันไทม์ซึ่งจะวาง URL ของ api ที่สร้างไว้ในตัวแปรส่วนกลาง วันนี้เราขับเคลื่อน OOP มากขึ้นเล็กน้อยและไม่ได้ใช้พื้นที่เก็บข้อมูลในตัวเครื่องเพื่ออะไรเลย
ฉันสร้างโซลูชันที่ดีกว่าสำหรับทั้งการกำหนดสภาพแวดล้อมและการสร้าง URL ของ API
สิ่งนี้แตกต่างกันอย่างไร?
แอปจะไม่โหลดเว้นแต่จะโหลดไฟล์ config.json ใช้ฟังก์ชันของโรงงานเพื่อสร้าง SOC ในระดับที่สูงขึ้น ฉันสามารถห่อหุ้มสิ่งนี้เป็นบริการได้ แต่ฉันไม่เคยเห็นเหตุผลใด ๆ เมื่อความคล้ายคลึงกันเพียงอย่างเดียวระหว่างส่วนต่างๆของไฟล์คือสิ่งเหล่านี้มีอยู่ด้วยกันในไฟล์ การมีฟังก์ชั่นโรงงานทำให้ฉันสามารถส่งผ่านฟังก์ชันไปยังโมดูลได้โดยตรงหากสามารถรับฟังก์ชันได้ สุดท้ายนี้ฉันมีเวลาที่ง่ายขึ้นในการตั้งค่า InjectionTokens เมื่อมีฟังก์ชั่นจากโรงงานให้ใช้งานได้
ข้อเสีย?
คุณโชคไม่ดีที่ใช้การตั้งค่านี้ (และคำตอบอื่น ๆ ส่วนใหญ่) หากโมดูลที่คุณต้องการกำหนดค่าไม่อนุญาตให้ส่งฟังก์ชันจากโรงงานไปยัง forRoot () หรือ forChild () และไม่มีวิธีอื่นในการ กำหนดค่าแพ็คเกจโดยใช้ฟังก์ชั่นโรงงาน
คำแนะนำ
- การใช้ fetch เพื่อดึงไฟล์ json ฉันเก็บวัตถุในหน้าต่างและเพิ่มเหตุการณ์ที่กำหนดเอง - อย่าลืมติดตั้ง whatwg-fetch และเพิ่มลงใน polyfills.ts ของคุณเพื่อความเข้ากันได้ของ IE
- ให้ผู้ฟังเหตุการณ์ฟังเหตุการณ์ที่กำหนดเอง
- ผู้ฟังเหตุการณ์ได้รับเหตุการณ์ดึงวัตถุจากหน้าต่างเพื่อส่งผ่านไปยังสิ่งที่สังเกตได้และล้างสิ่งที่เก็บไว้ในหน้าต่าง
- Bootstrap เชิงมุม
- นี่คือจุดที่การแก้ปัญหาของฉันเริ่มแตกต่างไปจากเดิม -
- สร้างไฟล์ที่ส่งออกอินเทอร์เฟซที่มีโครงสร้างแสดงถึง config.json ของคุณซึ่งช่วยในเรื่องความสอดคล้องของประเภทและส่วนถัดไปของโค้ดต้องใช้ประเภทและอย่าระบุ
{}
หรือany
เมื่อคุณรู้ว่าคุณสามารถระบุสิ่งที่เป็นรูปธรรมมากขึ้นได้
- สร้าง BehaviorSubject ที่คุณจะส่งผ่านไฟล์ json ที่แยกวิเคราะห์ในขั้นตอนที่ 3
- ใช้ฟังก์ชันจากโรงงานเพื่ออ้างอิงส่วนต่างๆของการกำหนดค่าของคุณเพื่อรักษา SOC
- สร้าง InjectionTokens สำหรับผู้ให้บริการที่ต้องการผลการทำงานจากโรงงานของคุณ
- และ / หรือ -
- ส่งผ่านฟังก์ชั่นจากโรงงานไปยังโมดูลที่สามารถรับฟังก์ชันได้ทั้งในเมธอด forRoot () หรือ forChild ()
- main.ts
ฉันตรวจสอบหน้าต่าง ["สภาพแวดล้อม"] ไม่ได้รับการเติมข้อมูลก่อนที่จะสร้างตัวฟังเหตุการณ์เพื่อให้ความเป็นไปได้ของโซลูชันที่มีการเติมหน้าต่าง ["สภาพแวดล้อม"] ด้วยวิธีการอื่นก่อนที่โค้ดใน main.ts จะดำเนินการ
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));
});
}
--- สภาพแวดล้อม resolvers.ts
ฉันกำหนดค่าให้กับ BehaviorSubject โดยใช้ window ["environment"] เพื่อความซ้ำซ้อน คุณสามารถคิดค้นโซลูชันที่กำหนดค่าของคุณไว้ล่วงหน้าแล้วและหน้าต่าง ["สภาพแวดล้อม"] จะถูกเติมไว้แล้วเมื่อมีการเรียกใช้โค้ดแอป Angular ของคุณรวมถึงโค้ดใน 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) {
}
return resolvedEnvironment;
}
export function resolveNgxLoggerConfig() {
return configurationSubject.getValue().logging;
}
- app.module.ts - ถอดออกเพื่อให้เข้าใจง่ายขึ้น
ช่วงเวลาสนุก! NGXLogger เวอร์ชันเก่ากว่ากำหนดให้คุณส่งผ่านวัตถุไปยัง LoggerModule.forRoot () ในความเป็นจริง LoggerModule ยังคงทำ! NGXLogger กรุณาแสดง LoggerConfig ซึ่งคุณสามารถแทนที่เพื่อให้คุณใช้ฟังก์ชันจากโรงงานในการตั้งค่าได้
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
ภาคผนวก
ฉันจะแก้ปัญหาการสร้าง URL API ของฉันได้อย่างไร
ฉันต้องการที่จะเข้าใจว่าแต่ละ URL ทำอะไรผ่านความคิดเห็นและต้องการตรวจสอบการพิมพ์เนื่องจากเป็นจุดแข็งที่ยิ่งใหญ่ที่สุดของ TypeScript เมื่อเทียบกับจาวาสคริปต์ (IMO) ฉันยังต้องการสร้างประสบการณ์ให้กับนักพัฒนาคนอื่น ๆ เพื่อเพิ่มจุดสิ้นสุดใหม่และ apis ที่ราบรื่นที่สุด
ฉันสร้างคลาสที่อยู่ในสภาพแวดล้อม (dev, test, stage, prod, "" และอื่น ๆ ) และส่งผ่านค่านี้ไปยังชุดของคลาส [1-N] ซึ่งมีหน้าที่สร้าง url พื้นฐานสำหรับแต่ละคอลเล็กชัน API . ApiCollection แต่ละตัวมีหน้าที่สร้าง URL พื้นฐานสำหรับแต่ละคอลเล็กชันของ API อาจเป็น API ของเราเอง API ของผู้ขายหรือแม้แต่ลิงก์ภายนอก คลาสนั้นจะส่ง URL พื้นฐานที่สร้างขึ้นไปยังแต่ละ api ที่ตามมาซึ่งมีอยู่ อ่านโค้ดด้านล่างเพื่อดูตัวอย่างกระดูกเปลือย เมื่อตั้งค่าแล้วมันง่ายมากสำหรับนักพัฒนารายอื่นในการเพิ่มจุดสิ้นสุดอื่นในคลาส Api โดยไม่ต้องสัมผัสสิ่งอื่นใด
TLDR; หลักการ OOP พื้นฐานและผู้ขี้เกียจสำหรับการเพิ่มประสิทธิภาพหน่วยความจำ
@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/");
}
}