Naučme sa, ako fungujú balíčky modulov, a potom si jeden napíšme sami

Ahoj! Vitajte, vitajte, je skvelé mať vás tu! Dnes budeme budovať skutočne jednoduchý zväzok modulov JavaScriptu.

Skôr ako začneme, chcem dať niekoľko poďakovaní. Tento článok vo veľkej miere čerpá z nasledujúcich zdrojov:

  • Oddelenie balíka modulov JavaScript - Luciano Mammino
  • Minipack - Ronen Amiel

Dobre, začnime s tým, čo balíček balíkov modulov v skutočnosti je.

Čo je modul bundler?

Zväzok modulov je nástroj, ktorý preberá časti kódu JavaScript a ich závislosti a zoskupuje ich do jedného súboru, zvyčajne na použitie v prehliadači. Možno ste použili nástroje ako Browserify, Webpack, Rollup alebo jeden z mnohých ďalších.

Zvyčajne sa začína vstupným súborom a odtiaľ sa zhromažďuje všetok kód potrebný pre tento vstupný súbor.

Existujú dve hlavné fázy zväzovača:

  1. Riešenie závislostí
  2. Balenie

Počnúc vstupným bodom (napríklad app.jsvyššie) je cieľom riešenia závislostí vyhľadať všetky závislosti vášho kódu (ďalšie časti kódu, ktoré musí fungovať) a zostaviť graf (nazývaný graf závislostí).

Keď to urobíte, môžete svoj graf závislostí zbaliť alebo previesť do jedného súboru, ktorý môže aplikácia použiť.

Začnime náš kód niekoľkými importmi (dôvod uvediem neskôr).

Riešenie závislostí

Prvá vec, ktorú musíme urobiť, je vymyslieť, ako chceme reprezentovať modul počas fázy riešenia závislostí.

Zastúpenie modulu

Budeme potrebovať štyri veci:

  • Názov a identifikátor súboru
  • Odkiaľ súbor pochádza (v súborovom systéme)
  • Kód v súbore
  • Aké závislosti tento súbor potrebuje

Štruktúra grafu sa vytvára rekurzívnou kontrolou závislostí v každom súbore.

V JavaScripte je najjednoduchší spôsob, ako reprezentovať takúto množinu údajov, objekt.

Pri pohľade na createModuleObjectvyššie uvedenú funkciu je pozoruhodnou časťou volanie volanej funkcie detective.

Detective je knižnica, ktorá dokáže nájsť všetky volania vyžadujúce () bez ohľadu na to, ako hlboko sú vnorené a jej použitie znamená, že sa môžeme vyhnúť vlastnému prechodu AST!

Jedna vec, ktorú treba poznamenať (a to je rovnaké takmer vo všetkých balíkoch modulov), je, že ak sa pokúsite urobiť niečo čudné, ako napríklad:

const libName = 'lodash'const lib = require(libName)

Nebude ho môcť nájsť (pretože by to znamenalo vykonať kód).

Čo teda dáva spustenie tejto funkcie z cesty modulu?

Čo bude ďalej? Riešenie závislostí.

Dobre, ešte nie. Najprv by som chcel hovoriť o veci, ktorá sa volá mapa modulu.

Mapa modulov

Keď importujete moduly do Uzla, môžete vykonávať relatívny import, napríklad require('./utils'). Takže keď to váš kód volá, ako zväzátor vie, čo je správny ./utilssúbor, keď je všetko zabalené?

To je problém, ktorý rieši mapa modulov.

Náš objekt modulu má jedinečný idkľúč, ktorý bude našim „zdrojom pravdy“. Takže keď robíme naše riešenie závislostí, pre každý modul budeme viesť zoznam mien požadovaných údajov spolu s ich ID. Týmto spôsobom môžeme získať správny modul za behu.

To tiež znamená, že môžeme všetky moduly uložiť do nevnoreného objektu, pričom ako kľúč použijeme id.

Riešenie závislostí

Dobre, takže vo getModulesfunkcii sa deje slušné množstvo . Jeho hlavným účelom je začať v module root / entry a rekurzívne hľadať a riešiť závislosti.

Čo mám na mysli pod pojmom „riešenie závislostí“? V uzle existuje vec, ktorá sa volá require.resolvea podľa toho uzol zistí, kde sa nachádza požadovaný súbor. Je to tak preto, lebo môžeme importovať relatívne alebo z node_modulespriečinka.

Našťastie pre nás existuje pomenovaný modul NPM, resolvektorý tento algoritmus implementuje za nás. Musíme len odovzdať argumenty závislostí a základných adries URL a urobí to za nás všetku ťažkú ​​prácu.

Toto uznesenie musíme vykonať pre každú závislosť každého modulu v projekte.

Vytvárame tiež mapu modulov s názvom, mapktorú som už spomínal.

Na konci funkcie nám zostane pole s názvom, modulesktoré bude obsahovať objekty modulov pre každý modul / závislosť v našom projekte.

Teraz, keď to máme, môžeme prejsť k poslednému kroku: balenie!

Balenie

V prehliadači neexistuje nič také ako moduly. To však znamená, že neexistuje žiadna požadovaná funkcia a nie module.exports. Takže aj keď máme všetky naše závislosti, v súčasnosti ich nemáme ako moduly.

Továrenská funkcia modulu

Zadajte výrobnú funkciu.

Továrenská funkcia je funkcia (ktorá nie je konštruktorom), ktorá vracia objekt. Je to vzor z objektovo orientovaného programovania a jedným z jeho použití je vykonávanie zapuzdrenia a vkladania závislostí.

Znie to dobre?

Pomocou továrenskej funkcie môžeme vložiť našu vlastnú requirefunkciu aj module.exportsobjekt, ktorý je možné použiť v našom pribalenom kóde, a dať modulu vlastný rozsah.

Balenie

Nasleduje funkcia balenia, ktorá sa používa na zabalenie.

Väčšina z nich sú iba šablónové literály JavaScriptu, takže poďme diskutovať o tom, čo robí.

Prvý na rade je modulesSource. Tu prechádzame každý z modulov a transformujeme ich do viacerých zdrojov.

Aký je teda výstup pre objekt modulu?

Teraz sa to číta trochu ťažko, ale vidíte, že zdroj je zapuzdrený. Poskytujeme modulesa requirepoužívame továrenské funkcie, ako som už spomínal.

Zahŕňame tiež mapu modulov, ktorú sme zostrojili počas riešenia závislosti.

Ďalej vo funkcii spojíme všetky tieto a vytvoríme veľký objekt všetkých závislostí.

Ďalším reťazcom kódu je IIFE, čo znamená, že keď tento kód spustíte v prehliadači (alebo kdekoľvek inde), funkcia sa spustí okamžite. IIFE je ďalší vzor na zapuzdrenie rozsahu a používa sa tu, aby sme neznečisťovali globálny rozsah pomocou našich requirea modulov.

Vidíte, že definujeme dve vyžadujúce funkcie, requirea localRequire.

Vyžadovať akceptuje ID objektu modulu, ale zdrojový kód sa samozrejme nepíše pomocou ID. Namiesto toho používame inú funkciu localRequirena prijatie akýchkoľvek argumentov, ktoré majú moduly vyžadovať, a na ich prevedenie na správne ID. Toto používa tieto mapové moduly.

Potom definujeme a, module objectktoré sa modul môže naplniť, a odovzdáme obe funkcie do továrne, po ktorej sa vrátime module.exports.

Nakoniec zavoláme require(0)vyžadovať modul s ID 0, čo je náš vstupný súbor.

A je to! Náš balíkovač modulov je 100% úplný!

Blahoželáme! ?

Takže teraz máme zväzok pracovných modulov.

Toto by sa vo výrobe pravdepodobne nemalo používať, pretože v ňom chýba veľa funkcií (napríklad správa kruhových závislostí, zabezpečenie toho, aby sa každý súbor analyzoval iba raz, es-moduly atď.), Ale dúfam, že vám to poskytne dobrú predstavu o tom, ako balíkovače modulov skutočne fungujú.

Ten v skutočnosti funguje asi na 60 riadkoch, ak odstránite všetok zdrojový kód.

Ďakujeme za prečítanie a dúfam, že sa vám páčil pohľad na fungovanie nášho jednoduchého balíka modulov. Ak ste to urobili, nezabudnite tlieskať? a zdieľať.

Tento článok bol pôvodne uverejnený na mojom blogu.

Skontrolujte zdroj //github.com/adamisntdead/wbpck-bundler