angular - Typescript - Kjør kode i filer uten å importere dem


Er det noen måte jeg kan kjøre koden i filer uten å importere dem i TypeScript (Angular-prosjekt)? Kanskje ved å spesifisere et filmønster gjennom en konfigurasjon i tsconfig?

Det jeg ønsker å gjøre er å registrere noen klasser i et globalt objekt, men jeg vil ikke opprettholde listen over klasser i én enkelt fil. Jeg vil heller ha en enkel kodelinje under hver klassedefinisjon som dette:

export class MyClassToRegister {...}

GlobalRegistry.register(MyClassToRegister);

Jeg forstår at denne koden vil bli kjørt når filen importeres, men noen ganger er det bare for sent. Noen som har forslag til hvordan dette kan oppnås?

Svar #1

Ja, ved å bruke Webpacks require.context(...) kan du importere en katalog med filer inn i pakken din.

Rask merknad: Du importerer fortsatt filene til pakken, men du trenger bare ikke å definere hver importsti statisk eller manuelt holde dem oppdatert hvis du legger til/fjerner filer.

Filstruktur:

La oss jobbe med denne eksempelfilstrukturen:

src/
    items/
        item1.ts
        item2.ts
        item3.ts
    registry.ts
    index.ts

Her er elementene vi kommer til å kreve fra en katalog:

//item1.ts, item2.ts, item3.ts
import GlobalRegistry from "../registry";

export class Item1 {
    //...
}
GlobalRegistry.register(Item1);

De lastede elementene vil registrere seg selv med denne tjenesten (eller hva enn din forretningslogikk er) - dette beviser at elementene blir lastet:

//registry.ts
export default class GlobalRegistry {

    static _items = [];

    static register(cls){
        console.log('Register class: ', cls.name);
        this._items.push(cls.name);
    }

    static getItems(){
        return this._items;
    }

}

require.context(...)

Bruk require.context(...) for å kreve alle filer under katalogen 'items':

//index.ts
import GlobalRegistry from './registry';

// Import all files under './items/*.ts'
var context = require.context('./items', true, /\.ts$/);
context.keys().forEach((key) => {
    context(key);
});

console.log('Loaded classes:', GlobalRegistry.getItems());

Til slutt, for å holde TypeScript fornøyd, deklarerer vi require.context()-grensesnittet som leveres av Webpack:

//references.d.ts

// Extend @types/node NodeRequire type to define Webpack's context function
declare interface NodeRequire {
    context: (dir: string, includeSubdirs: boolean, filter: RegExp) => any;
}

// Tell TypeScript that there is a global `require` variable available to us
declare var require: NodeRequire;

Resultat :

Når applikasjonen kjører, bør du se dette logget ut:

Register class:  Item1
Register class:  Item2
Register class:  Item3
Loaded classes: (3) ["Item1", "Item2", "Item3"]

Notater :

1. Rekkkefølge på inkluderingsfiler Rekkkefølgen på inkluderinger er ikke garantert hvis du refererer til individuelle klasser først.

For eksempel, hvis du eksplisitt importerer en type og bruker den som en verdi, vil den typen bli lastet før andre som bare er inkludert via require.context(...).

Eksempel - bruk av Item2 som en verdi:

//index.ts

/* require.context(...) stuff is here */

import {Item2} from './items/Item2';
let myItem = new Item2();   // use Item2 as a value

Endrer lastrekkefølgen:

Register class:  Item2
Register class:  Item1
Register class:  Item3
Loaded classes: (3) ["Item2", "Item1", "Item3"]

Men merk at bare å referere etter type (og ikke etter verdi) vil ikke endre lastrekkefølgen

let myItem: Item2;  // just referencing by type won't affect load order

2. Advarsel om påkrevd funksjonsavhengighet

Du kan få en advarsel under bygging som: require-funksjonen brukes på en måte der avhengigheter ikke kan hentes ut statisk.

Det er ikke nødvendigvis noe galt med det, det er bare Webpack som lar deg vite at du gjør noe rart med require - noe som er sant :)

Å gjøre denne dynamiske importen kan påvirke tree-shaking eller annen statisk analyse av bundelen din (dvs. disse typene kan ikke ekskluderes fra bundelen hvis de ikke brukes). Men det kan være en rimelig avveining for å slippe å manuelt håndtere filimportene dine - du må vurdere det opp mot dine egne krav/prosjektmål.

Svar #2

En løsning ville være å bruke multifile namespaces som tillater å dele kode inn i flere filer uten å måtte importere moduler.

Her er et forenklet eksempel hentet fra TypeScript-dokumentasjonen om navnerom.

Validation.ts-fil

namespace Validation {
    export interface StringValidator {
        isAcceptable(s: string): boolean;
    }
}

LettersOnlyValidator.ts-fil

namespace Validation {
    const lettersRegexp = /^[A-Za-z]+$/;
    export class LettersOnlyValidator implements StringValidator {
        isAcceptable(s: string) {
            return lettersRegexp.test(s);
        }
    }
}

Relaterte artikler