Hvordan man bygger og udgiver et vinkelmodul

Da jeg oprettede kantet-async-lokal opbevaring, var det let at oprette et vinkelmodul og bruge det direkte i min app. Men da det kunne hjælpe andre udviklere, ønskede jeg, at det skulle være et genanvendeligt modul, pakket og konsumeret som alle andre vinkelmoduler.

Jeg kæmpede med denne bygningsdel. Jeg fandt næsten ingen dokumentation om dette, så jeg prøvede at kopiere, hvordan det officielle Http-modul fungerer. Nu er det gjort, jeg deler min oplevelse om, hvordan man bygger og udgiver et vinkelmodul.

Dette indlæg er til erfarne udviklere, der allerede kender kernekoncepterne i Angular og hvordan man opretter en grundlæggende app. En fransk version af dette indlæg er tilgængeligt her.

Opdatering: Angular 6

Dette indlæg er ikke længere relevant. Med Angular 6 er oprettelse af et kantet bibliotek så simpelt som navnet på biblioteket. Se officiel CLI-dokumentation.

Af den samme forfatter

  • Vinkelskematisk udvidelse til VS-kode: GUI for vinklede CLI-kommandoer
  • @ ngx-pwa / local-storage: 1. vinkelbibliotek til lokal opbevaring
  • Andre kantede biblioteker: @ ngx-pwa / offline & @ ngx-pwa / ngsw-schema
  • Andre populære kantede indlæg på Medium
  • Følg mig på Twitter
  • Vinkeltræning på stedet (med base i Paris, så webstedet er på fransk, men min engelske biograf er her, og jeg er åben for at rejse)

Oprettelse af dit vinkelmodul: faldgruber

Denne del er ret den samme som at oprette et modul i din app: importer de moduler, du har brug for, erklær komponenter, direktiver eller rør eller leverer nogle tjenester. Der er kun et par punkter man skal være opmærksom på.

Først skal du aldrig importere BrowserModule. Dit modul er et funktionsmodul, kun den endelige bruger skal importere BrowserModule i approt-modulet. Hvis du har brug for de fælles direktiver (* ngIf, * ngFor ...), skal du importere CommonModule.

Hvis dit modul handler om at oprette nye komponenter, direktiver eller rør, så glem ikke at eksportere dem. Deklarerede er kun tilgængelige i dit modul.

Det vigtigste er, at du ikke blander komponenter / direktiver / rør og tjenester i det samme modul. Hvorfor?

  • En service, der leveres i et modul, vil være tilgængelig overalt i appen, så dit modul skal kun importeres en gang i bruger-app-rodmodulet (som Http-modulet).
  • En eksporteret komponent / direktiv / pipe vil kun være tilgængelig i modulet, der importerer dit, så dit modul skal importeres i hvert brugermodul (rod- ​​og / eller funktionsmoduler), der har brug for dem (som CommonModule).

Hvis dette ikke er klart for dig, skal du mit andet indlæg "Forstå vinkelmoduler (NgModule) og deres anvendelsesområder", da det er et vigtigt (og forvirrende) punkt i vinkel.

Til sidst skal du respektere vinkelreglen: Brug aldrig browserspecifikke API'er (som DOM) direkte. Hvis du gør det, er dit modul ikke kompatibelt med Universal-server gengivelse og andre vinklede avancerede indstillinger. Hvis du virkelig har brug for at bruge browser-specifikke API'er (localStorage ...), skal du prøve / fange fejl.

Eksport af det offentlige API

Når du bruger et officielt vinkelmodul, har du bare et indgangspunkt for at importere alt det, du har brug for (som '@ vinkel / http').

Så du bliver nødt til at oprette en index.ts-fil, der eksporterer hele det offentlige API for dit modul. Det skal mindst indeholde din NgModule og dine komponenter eller tjenester (brugeren skal importere dem for at injicere dem, hvor de er nødvendige).

Komponenter / direktiver / rør importeres ikke direkte af brugeren, men du skal eksportere dem for at være AoT-kompatible (tak til Isaac Mann for denne info).

Byg værktøjer

Det var her jeg begyndte at kæmpe. Så det lykkedes mig at kopiere, hvordan officielle vinkelmoduler fungerer, som HttpModule. De bruger:

  • typeskrift via vinkelkompilatoren (ngc) til transpilering,
  • rulllupjs til emballering,
  • uglify-js til minificering.
npm installer @ vinkel / kompilator @ vinkel / kompilator-cli-typen samle uglify-js - save-dev

TypeScript-konfiguration

Her er tsconfig.json i mit modul:

Der er nogle vigtige forskelle med din klassiske tsconfig.json:

  • Der kræves eksplicitte "stier" til andre moduler, du bruger, da det endelige bundt ikke inkluderer dem direkte (mere om det senere).
  • "angularCompilerOptions": {"strictMetadataEmit": true} er nødvendig for at være AoT-kompatibel.
  • "deklaration": sandt er vigtigt for at generere typedefinitionsfiler, så brugeren har Intellisense til dit modul.
  • "noImplicitAny": true og "strictNullChecks": true anbefales for at undgå fejl og for at være kompatible med alle brugerkonfigurationer. "noImplicitAny": true skal respekteres siden Angular 4.0, og "strictNullChecks": true startende fra Angular 4.1.
  • "module": "es2015" er vigtig for ydeevnen og "sourceMap": sandt til fejlfinding, men intet specifikt her.
  • "stripInternal": sand undgå unyttige erklæringer for interne API'er og "skipLibCheck": sand undgå at blive blokeret af (ufarlige) fejl i de biblioteker, du bruger.

Samlingskonfiguration

Vinkelmoduler leveres i UMD-format, så din rulllup.config.js skal indstilles som følge heraf. Her er et eksempel:

Indgangsscriptet er dine transpilerede index.ts, så det skal matche din TypeScript-konfiguration. bundles / modulename.umd.js er den konventionelle sti og navn, der bruges af vinkelmoduler.

Samling kræver et modulnavn til UMD-format. Det vil være et JavaScript-objekt, så brug ikke specialtegn (ingen bindestreger).

Derefter er det, hvor det vigtige punkt finder sted. Dit modul bruger kantede ting (i det mindste NgModule-dekoratøren), men dit bundt skal ikke indeholde vinkelformet.

Hvorfor? Vinkelformet vil allerede være inkluderet af brugerappen. Hvis dit modul også inkluderer det, vil det være der to gange, og der vil være fatale (og uforståelige) fejl.

Så du er nødt til at indstille Angular som en global. Og du skal kende UMD-modulets navn for hvert modul. Det følger denne konvention: ng.modulename (ng.core, ng.common, ng.http ...).

Det samme gælder RxJS, hvis dit modul bruger det. Og modulnavne er meget rod her. For klasser (observerbar ...) er det Rx. For operatører (kort, filter…) er det Rx.Observable.prototype. For direkte metoder til klasser (af, fra Event ...) er det Rx.Observable.

Endelig bygger

Du kan nu bygge dit modulbundt. Du kan gemme kommandolinjer i npm-scripts:

Derefter:

npm run build

Bemærk, at transpilering ikke sker direkte af TypeScript, du skal bruge Angular compiler (ngc): det er TypeScript med nogle yderligere kantede magier.

Udgivelse kl

Offentliggør ikke alt på npm, kun dist-biblioteket.

Du skal oprette en ny og specifik dist / package.json. For eksempel :

Nogle specifikke punkter:

  • "version" skal følge semantisk versionering. Enhver ændring af brud betyder et stort antal forøgelser (selvom det er en lille ændring). Og når du ændrer dit modul for at holde dig opdateret med Angular, er det et mindre antal.
  • "hoved" og "modul" stier er nødvendige for brugerimport. "typings" sti er til Intellisense.
  • "licens": "MIT": en open source-licens er vigtig, eller dit modul er nytteløst. Angular bruger MIT-licensen, og du skal holde dig til den.
  • Vinkelmoduler, du har brugt, vil blive vist i peerafhængigheden. Følg stadig semver, med ^ -tegnet, ellers bliver dit modul forældet hver gang vinkelopgraderinger. For andre biblioteker (RxJS, zone.js ...) kan du se de aktuelle krav til Angular her.

Glem ikke at skrive en README med dokumentationen til din API. Ellers er dit modul ubrugeligt. Du kan bruge et bibliotek som copyfiles til at kopiere dit README fra dit rodprojektkatalog (vises på Github) til dit dist-bibliotek (vises på npm-arkivet).

Og med en konfigureret npm-konto kan du nu offentliggøre dit modul:

cd dist
npm offentliggør

Og når som helst du har brug for at opdatere dit modul, er det bare at genopbygge, ændre versionens nummer, opdatere changelog og offentliggøre igen.