Kodedeling i vinkelformat, eller hvordan man deler komponenter mellem doble moduler

Denne artikel giver dig en bedre forståelse af, hvordan Angular opdeler din kode i biter.

Hvis du er bange for Angular CLI output vist ovenfor, eller hvis du er nysgerrig efter, hvordan den kodespaltning faktisk sker, så er dette indlæg noget for dig.

Kodespaltning giver dig mulighed for at opdele din kode i forskellige bundter, som derefter kan indlæses efter behov. Hvis det bruges korrekt, kan det have en stor indflydelse på belastningstiden.

Indhold

  1. Hvorfor skulle jeg passe mig?
  2. Vinklet CLI-kodespaltning under hætten
  3. Enkel vinkel applikation med doven moduler
  4. Sådan deler du komponenter mellem doven moduler
  5. Konklusion

Hvorfor skulle jeg passe mig?

Lad os forestille os, at du startede et helt nyt vinkelprojekt. Du læser mange ressourcer om, hvordan man arkituerer en vinkel-applikation, hvad der er den passende mappestruktur, og hvad der er vigtigst, hvordan man opretholder en god opstartydelse.

Du valgte Angular CLI og oprettede en modulær applikation med masser af doble-ladede funktionsmoduler. Og selvfølgelig oprettede du et delt modul, hvor du satte almindeligt anvendte direktiver, rør og komponenter.

Efter et stykke tid fangede du dig selv i at tænke, at når dit nye funktionsmodul først kræver en vis funktionalitet fra andre funktionsmoduler, har du en tendens til at flytte denne funktionalitet til det samme delte modul.

Applikationen udvikler sig, og snart bemærkede du, at dens starttid ikke lever op til din (og vigtigst af alt, din klient) forventning.

Nu er du i tvivl ...

  • Hvis jeg lægger alle mine rør, direktiver og almindelige komponenter i et stort delt modul og derefter importerer det i doble-ladede moduler (hvor jeg kun bruger en eller to af de importerede funktioner) kan det sandsynligvis forårsage duplikater med ubrugt kode i outputfilerne .
  • På den anden side, hvis jeg delt delte funktioner mellem flere delte moduler og kun importerer dem der er nødvendige i hvert bestemt modul, vil det reducere størrelsen på min app? Eller angular gør alle sådanne optimeringer som standard?

Lad os afmystificere!

Vinklet CLI-kodespaltning under hætten

Som vi alle ved, bruger den aktuelle Angular CLI-version webpack til at udføre bundling. Men trods det er webpack også ansvarlig for kodespaltning.

Så lad os se på, hvordan webpack gør det.

Webpack 4 introducerede SplitChunksPlugin, der giver os mulighed for at definere nogle heuristikker til at opdele moduler i bidder. Mange klager over, at denne konfiguration virker mystisk. Og på samme tid er dette den mest interessante del af kodespaltning.

Men inden SplitChunksPlugin-optimering anvendes, skaber webpack en ny del:

  • for hvert indgangspunkt

Vinkelformet CLI konfigurerer følgende indgangspunkter

vigtigste polyfillestilarter

hvilket vil resultere i bidder med de samme navne.

  • for hvert dynamisk indlæst modul (ved hjælp af import () -syntaks, der er i overensstemmelse med ECMAScript-forslaget til dynamisk import)

Kan du huske indlæsning af børns syntaks? Dette er signalet for at webpack skaber en del.

Lad os nu gå videre til SplitChunksPlugin. Det kan aktiveres inden i optimeringsblok af webpack.config.js

Lad os se på Angular CLI-kildekode og finde det konfigurationsafsnit:

SplitChunksPlugin-konfiguration i vinkel CLI 8

Vi vil fokusere på cacheGroups indstillinger her, da dette er "opskriften" til webpack om, hvordan man opretter adskilte bidder baseret på nogle betingelser.

cacheGroups er et almindeligt objekt, hvor nøglen er et gruppenavn. Grundlæggende kan vi tænke på en cache-gruppe som en potentiel mulighed for at oprette en ny del.

Hver gruppe har mange konfigurationer og kan arve konfiguration fra splitChunks-niveau.

Lad os gå rigtig hurtigt ud over de muligheder, vi så i Angular CLI-konfiguration ovenfor:

  • stykkeværdi kan bruges til at filtrere moduler mellem synkronisering og asynkebiter. Dets værdi kan være initial, async eller alt. initial betyder kun at tilføje filer til chunk, hvis de importeres i synkroniseringsbiter. async betyder kun at tilføje filer til stykket, hvis de importeres inde i asyncebunker (async som standard)
  • minChunks fortæller webpack om kun at injicere moduler i chunk, hvis de deles mellem mindst 2 bidder (1 som standard)
  • navn fortæller webpack at bruge dette navn til en nyoprettet del. Specificering af enten en streng eller en funktion, der altid returnerer den samme streng, fletter alle almindelige moduler til en enkelt del.
  • prioritetsværdi bruges til at identificere de bedst matchede bunker, når et modul falder ind under mange stykkegrupper.
  • håndhæve fortæller webpack at ignorere indstillinger for minSize, minChunks, maxAsyncRequests og maxInitialRequests og altid oprette bunker til denne cache-gruppe. Der er en lille gotcha her: hvis nogen af ​​disse ignorerede indstillinger leveres på cacheGroup-niveau, vil denne indstilling stadig blive brugt.
  • test styrer, hvilke moduler der vælges af denne cache-gruppe. Som vi kunne bemærke, bruger Angular CLI denne mulighed til at flytte alle node_modules afhængigheder til leverandørpartiet.
  • minSize bruges til at identificere minimumsstørrelse, i byte, for en del, der skal genereres. Det vises ikke i Angular CLI config, men det er en meget vigtig mulighed, som vi skal være opmærksomme på. (Som det fremgår af kildekoden, er det 30 kb som standard i produktionen og 10 kb i dev miljø)
Tip: på trods af at webpack-dokumentationen definerer standardindstillinger, henviser jeg til webpack-kildekoden for at finde de nøjagtige værdier

Lad os sammenfatte her: Angular CLI flytter et modul til:

  • leverandørparti, hvis dette modul kommer fra biblioteket for node_moduler.
  • standardstykke, hvis dette modul importeres i et async-modul og deles mellem mindst to moduler. Bemærk, at mange standardbunker er mulige her. Jeg vil forklare, hvordan webpack genererer navne på disse biter senere.
  • fælles chunk, hvis dette modul importeres i et async-modul og deles mellem mindst to moduler og ikke falder ind under standard-chunk (hej-prioritet) og også uanset hvilken størrelse det er (takket være optionen theenforce)

Nok teori, lad os øve.

Enkel vinkel applikation med doven moduler

For at forklare processen med SplitChunksPlugin vil vi starte med en forenklet version af Angular-applikationen:

app ├── a (doven) │ └── a.component.ts │ └── a.module.ts │ ├── ab │ └── ab.component.ts │ └── ab.module.ts │ ├── b (doven) │ └── b.component.ts │ └── b.module.ts │ └── c (doven) │ └── c.component.ts │ └── c.module. ts │ └── cd │ └── cd.component.ts │ └── cd.module.ts │ └── d (doven) │ └── d.component.ts │ └── d.module.ts │ └── delt │ └── shared.module.ts │ └── app.component.ts └── app.module.ts

Her er a, b, c og d dovne moduler, hvilket betyder, at de importeres ved hjælp af import () syntaks.

a og b-komponenter bruger ab-komponent i deres skabeloner. c- og d-komponenter bruger cd-komponent.

Afhængigheder mellem vinkelmoduler

Forskellen mellem ab.module og cd.module er, at ab.module importeres i a.module og b.module, mens cd.module importeres i shared.module.

Denne struktur beskriver nøjagtigt den tvivl, vi ønskede at afmystificere. Lad os finde ud af, hvor ab- og cd-moduler vil være i den endelige output.

Algoritme

1) SplitChunksPlugins algoritme starter med at give hver tidligere oprettede bidder et indeks.

bidder efter indeks

2) Derefter løber det over alle moduler i kompilering for at udfylde chunkSetsInGraph Map. Denne ordbog viser, hvilke bunker der har den samme kode.

chunkSetsInGraph

F.eks. Betyder 1,2 hoved, polyfill række, at der er mindst et modul, der vises i to bidder: main og polyfill.

a og b-moduler deler den samme kode fraab-modul, så vi kan også bemærke kombinationen (4,5) ovenfor.

3) Gå gennem alle moduler og find ud af, om det er muligt at oprette en ny del til en bestemt cacheGroup.

3a) Først og fremmest bestemmer webpack, om et modul kan føjes til specifik cacheGroup ved at kontrollere thecacheGroup.test-egenskaben.

ab.module tests

standardtest undefined => ok
almindelig test undefined => ok
leverandørtestfunktion => falsk

standard og fælles cache-gruppe definerede ikke den tætteste egenskab, så den skulle passere den. leverandørcache-gruppe definerer en funktion, hvor der er et filter, der kun inkluderer moduler fra thenode_modules-stien.

cd.module tests er de samme.

3b) Nu er det tid til at gå gennem alle chunk-kombinationer.

Hvert modul forstår, i hvilke bunker det vises (takket være module.chunksIterable egenskab).

ab.module importeres i to doven bidder. Så dens kombinationer er (4), (5) og (4,5).

På den anden side importeres cd.module kun i det delte modul, hvilket betyder, at det kun importeres i themain-stykke. Dens kombinationer er kun (1).

Derefter filtrerer plugin kombinationer efter minChunk-størrelse:

hvis (chunkCombination.size 

Da ab.module har kombinationen (4,5), skal den bestå denne kontrol. Dette kan vi ikke sige om cd.module. På dette tidspunkt forbliver dette modul tilbage i hovedstykket.

3c) Der er endnu en kontrol af cacheGroup.chunkds (initial, async eller alle)

ab.module importeres inde i async (dovne belastede) bidder. Dette er nøjagtigt hvad standard- og almindelige cache-grupper kræver. Denne måde ab.module er føjet til to nye mulige bidder (standard og fælles).

Jeg lovede det tidligere, så her går vi.

Hvordan genererer webpack navnet for en del, der er oprettet af SplitChunksPlugin?

Den forenklede version af det kan repræsenteres som

hvor:

  • groupName er navnet på gruppen (standard i vores tilfælde)
  • ~ er en standardAutomaticNameDelimiter
  • chunkNames refererer til listen over alle chunk-navne, der er inkluderet i denne gruppe. Dette navn er som en fullPath-sti, men i stedet for skråstreg bruger den -.

F.eks. Betyder dd-modul, at vi har d.module-fil i d-mappe.

Så efter at vi brugte import ('./ a / a.module') og import ('./ b / b.module') får vi

Struktur af standardstykketavn

En ting, der er værd at nævne, er, at når længden på et stykke navn når 109 tegn, skærer webpack det og tilføjer noget hash i slutningen.

Struktur af det store stykke navn, der deler kode på tværs af flere doble moduler

Vi er klar til at udfylde chunksInfoMap, der ved alt om alle mulige nye bunker og også ved, hvilke moduler det skal bestå af og relaterede bunker, hvor disse moduler i øjeblikket er bosiddende.

chunksInfoMap

Det er tid til at filtrere mulige bidder

SplitChunksPlugin-løkker over chunksInfoMap's genstande for at finde den bedste matchende post. Hvad betyder det?

standard cache-gruppe har en prioritet 10, som overvægter almindeligt (som kun har 5). Dette betyder, at standard er den bedste matchende post, og den skal behandles først.

Når alle andre krav er opfyldt, fjerner webpack alle moduler fra andre mulige bunker i chunksInfoMap-ordbogen. Hvis der ikke er noget modul tilbage, slettes modulet

Denne måde standard ~ aa-module ~ bb-module har forrang frem for den almindelige del. Sidstnævnte fjernes, da den indeholder den samme liste over moduler.

Sidste, men ikke mindst trin, er at foretage nogle optimeringer (som at fjerne duplikationer) og sørge for, at alle krav som maxSize er opfyldt.

Hele kildekoden til SplitChunksPlugin kan findes her

Vi har opdaget, at webpack skaber bunker på tre forskellige måder:

  • for hver post
  • til dynamisk indlæste moduler
  • til delt kode ved hjælp af SplitChunksPlugin
Vinkelformet CLI-output efter bunttype

Lad os nu vende tilbage til vores tvivl om, hvad der er den bedste måde at bevare delt kode på.

Sådan deler du komponenter mellem doven moduler

Som vi har set i vores enkle Angular-applikation, oprettede webpack separeret del til ab.module, men inkluderede cd.module i hoveddelen.

Lad os sammenfatte de vigtigste takeaways fra dette indlæg:

  • Hvis vi lægger alle delte rør, direktiver og fælles komponenter i et stort delt modul og derefter importerer det overalt (inde i synkroniserings- og asynkebunker), vil den kode være i vores oprindelige hovedbit. Så hvis du vil have en dårlig initial belastningsydelse, er det vejen at gå.
  • På den anden side, hvis vi opdeler den almindeligt brugte kode på doble lastede moduler, oprettes der en ny delt del, der kun indlæses, hvis nogen af ​​disse doble moduler er indlæst. Dette skal forbedre applikationens første belastning. Men gør det med omhu, fordi det undertiden er bedre at lægge en lille kode i en del, der har den ekstra anmodning, der er nødvendig for en separat del af belastningen.

Konklusion

Jeg håber, at du nu klart skal forstå output fra Angular CLI og skelne mellem indgang, dynamisk og splittet ved hjælp af SplitChunksPlugin-bidder.

God kodning!