Impara le basi del sistema dei moduli JavaScript e costruisci la tua libreria

Ultimamente abbiamo sentito molto parlare di "Moduli JavaScript". Probabilmente tutti si chiedono cosa farne e come possono svolgere un ruolo vitale nella nostra vita quotidiana ...?

Cosa diavolo è il sistema di moduli JS?

Man mano che lo sviluppo di JavaScript diventa sempre più diffuso, gli spazi dei nomi e le dipendenze diventano molto più difficili da gestire. Sono state sviluppate diverse soluzioni per affrontare questo problema sotto forma di sistemi di moduli.

Perché è importante comprendere il sistema di moduli JS?

Lascia che ti racconti una storia.

Raccontare storie è fondamentale per gli esseri umani quanto mangiare. Tanto più, infatti, mentre mentre il cibo ci fa vivere, le storie sono ciò che rende la nostra vita degna di essere vissuta: Richard Kearney

Perché sto anche parlando di tutte queste cose?
 
Quindi, il mio lavoro quotidiano è quello di progettare e progettare progetti, e ho capito rapidamente che c'erano molte funzionalità comuni richieste in tutti i progetti. Ho sempre finito per copiare e incollare quelle funzionalità su nuovi progetti ancora e ancora.

Il problema era che ogni volta che cambiava una parte del codice, dovevo sincronizzare manualmente quelle modifiche in tutti i miei progetti. Per evitare tutte queste noiose attività manuali, ho deciso di estrarre le funzionalità comuni e comporre da esse un pacchetto npm. In questo modo, altri membri del team sarebbero in grado di riutilizzarli come dipendenze e semplicemente aggiornarli ogni volta che veniva lanciata una nuova versione.

Questo approccio presenta alcuni vantaggi:

  • Se nella libreria principale sono state apportate modifiche, è necessario apportare una modifica in un solo punto senza refactoring di tutto il codice delle applicazioni per la stessa cosa.
  • Tutte le applicazioni sono rimaste sincronizzate. Ogni volta che veniva apportata una modifica, tutte le applicazioni dovevano solo eseguire il comando "npm update".
Codice sorgente della biblioteca

Quindi, il passo successivo è stato quello di pubblicare la biblioteca. Destra?

Questa è stata la parte più difficile, perché c'erano molte cose che mi rimbalzavano in testa, come:

  1. Come posso rendere l'albero agitabile?
  2. Quali sistemi di moduli JS dovrei scegliere come target (commonjs, amd, harmony).
  3. Devo traspilare la fonte?
  4. Devo raggruppare la fonte?
  5. Quali file devo pubblicare?

Ognuno di noi ha avuto questo tipo di domande gorgoglianti nelle nostre teste durante la creazione di una biblioteca. Destra?

Ora cercherò di rispondere a tutte le domande precedenti.

Diversi tipi di sistemi di moduli JS

1. CommonJS

  • Implementato dal nodo
  • Utilizzato per il lato server quando sono installati moduli
  • Nessun caricamento del modulo runtime / asincrono
  • importazione tramite "richiedi"
  • esportazione tramite "module.exports"
  • Quando importi, ricevi un oggetto
  • Nessun albero che scuote, perché quando si importa si ottiene un oggetto
  • Nessuna analisi statica, come si ottiene un oggetto, quindi la ricerca delle proprietà è in fase di esecuzione
  • Ottieni sempre una copia di un oggetto, quindi nessuna modifica in tempo reale nel modulo stesso
  • Cattiva gestione delle dipendenze ciclica
  • Sintassi semplice

2. AMD: definizione del modulo asincrono

  • Implementato da RequireJs
  • Utilizzato per il lato client (browser) quando si desidera il caricamento dinamico dei moduli
  • Importa tramite "richiedi"
  • Sintassi complessa

3. UMD: definizione del modulo universale

  • Combinazione di CommonJs + AMD (ovvero Sintassi di CommonJs + caricamento asincrono di AMD)
  • Può essere utilizzato per entrambi gli ambienti AMD / CommonJs
  • UMD essenzialmente crea un modo per utilizzare uno dei due, supportando anche la definizione della variabile globale. Di conseguenza, i moduli UMD sono in grado di funzionare sia su client che su server.

4. ECMAScript Harmony (ES6)

  • Utilizzato sia per il lato server / client
  • Caricamento runtime / statico dei moduli supportati
  • Quando si importa, si ottiene il valore di bind (valore effettivo)
  • Importa tramite "importa" ed esporta tramite "esporta"
  • Analisi statica: puoi determinare importazioni ed esportazioni in fase di compilazione (staticamente): devi solo guardare il codice sorgente, non devi eseguirlo
  • Albero scuotibile, grazie all'analisi statica supportata da ES6
  • Ottieni sempre un valore reale in modo che le modifiche in tempo reale nel modulo stesso
  • Migliore gestione delle dipendenze ciclica rispetto a CommonJS

Quindi ora sai tutto sui diversi tipi di sistemi di moduli JS e su come si sono evoluti.

Sebbene il sistema di moduli ES Harmony sia supportato da tutti gli strumenti e dai browser moderni, non sappiamo mai quando pubblichiamo librerie come i nostri consumatori potrebbero utilizzarle. Quindi dobbiamo sempre assicurarci che le nostre librerie funzionino in tutti gli ambienti.

Approfondiamo e progettiamo una libreria di esempio per rispondere a tutte le domande relative alla pubblicazione di una libreria nel modo corretto.

Ho creato una piccola libreria dell'interfaccia utente (puoi trovare il codice sorgente su GitHub) e condividerò tutte le mie esperienze ed esplorazioni per la compilazione, il raggruppamento e la pubblicazione.

Struttura delle directory

Qui abbiamo una piccola libreria UI che ha 3 componenti: Button, Card e NavBar. Trasmettiamo e pubblichiamo passo dopo passo.

Best practice prima di pubblicare

  1. Agitazione dell'albero
  • Tree shaking è un termine comunemente usato nel contesto di JavaScript per l'eliminazione del codice morto. Si basa sulla struttura statica della sintassi del modulo ES2015, ovvero importazione ed esportazione. Il nome e il concetto sono stati resi popolari dall'aggiornamento cumulativo del modulo ES2015.
  • Webpack e Rollup supportano entrambi Tree Shaking, il che significa che dobbiamo tenere a mente alcune cose in modo che il nostro codice sia agitabile.

2. Pubblica tutte le varianti del modulo

  • Dovremmo pubblicare tutte le varianti del modulo, come UMD ed ES, perché non sappiamo mai in quali versioni di browser / webpack i nostri consumatori potrebbero usare questa libreria / pacchetto.
  • Anche se tutti i bundler come Webpack e Rollup comprendono i moduli ES, se il nostro consumatore utilizza Webpack 1.x, non può comprendere il modulo ES.
// package.json
{
"name": "js-module-system",
"versione": "0.0.1",
...
"main": "dist / index.js",
"module": "dist / index.es.js",
...
}
  • Il campo principale del file package.json viene solitamente utilizzato per puntare alla versione UMD della libreria / pacchetto.
  • Potresti chiederti: come posso rilasciare la versione ES della mia libreria / pacchetto?
    Il campo del modulo di package.json viene utilizzato per puntare alla versione ES della libreria / pacchetto. In precedenza, molti campi venivano utilizzati come js: next e js: main, ma il modulo è ora standardizzato e viene utilizzato dai bundler come ricerca della versione ES della libreria / pacchetto.
Fatti meno noti: Webpack usa resol.mainfields per determinare quali campi in package.json sono controllati.
Suggerimento per le prestazioni: prova sempre a pubblicare anche la versione ES della tua libreria / pacchetto, poiché tutti i browser moderni ora supportano i moduli ES. In questo modo puoi trasferire meno, e alla fine finirai per spedire meno codice ai tuoi utenti. Ciò aumenterà le prestazioni della tua applicazione.

Quindi adesso qual è il prossimo? Transpilation o Bundling? Quali strumenti dovremmo usare?

Ah, ecco che arriva la parte più difficile! Immergiamoci.

Webpack vs Rollup vs Babel?

Questi sono tutti gli strumenti che utilizziamo nella nostra vita quotidiana per spedire le nostre applicazioni / librerie / pacchetti. Non riesco a immaginare lo sviluppo web moderno senza di loro - #blessed. Pertanto, non possiamo confrontarli, quindi sarebbe la domanda sbagliata da porre!

Ogni strumento ha i suoi vantaggi e serve a scopi diversi in base alle tue esigenze.

Diamo un'occhiata a ciascuno di questi strumenti ora:

Webpack

Webpack è un ottimo bundle di moduli ampiamente accettato e utilizzato principalmente per la creazione di SPA. Ti offre tutte le funzionalità predefinite come la suddivisione del codice, il caricamento asincrono dei bundle, lo scuotimento dell'albero e così via. Utilizza il sistema di moduli CommonJS.

PS: Webpack-4.0.0 alpha è già uscito . Speriamo che con il rilascio stabile diventerà il bundler universale per tutti i tipi di sistemi di moduli.

RollupJS

Rollup è anche un modulo bundler simile al Webpack. Tuttavia, il vantaggio principale del rollup è che segue la nuova formattazione standardizzata per i moduli di codice inclusi nella revisione ES6, quindi è possibile utilizzarlo per raggruppare la variante del modulo ES della propria libreria / pacchetto. Non supporta il caricamento asincrono di pacchetti.

Babele

Babel è un transpiler per JavaScript noto soprattutto per la sua capacità di trasformare il codice ES6 in codice che viene eseguito oggi nel browser (o sul server). Ricorda che si transpila e non raggruppa il codice.

Il mio consiglio: usa Rollup per librerie e Webpack per app.

Traspila (Babel-ify) la fonte o raggruppala

Di nuovo c'è una storia dietro questa.

Ho trascorso la maggior parte del mio tempo cercando di capire la risposta a questa domanda mentre stavo costruendo questa biblioteca. Ho iniziato a scavare i miei node_modules per cercare tutte le grandi librerie e controllare i loro sistemi di compilazione.

Librerie vs pacchetti creano un confronto di output

Dopo aver esaminato l'output di build per diverse librerie / pacchetti, ho avuto un quadro chiaro delle diverse strategie che gli autori di queste librerie avrebbero potuto avere in mente prima della pubblicazione. Di seguito sono le mie osservazioni.

Come puoi vedere nell'immagine sopra, ho diviso queste librerie / pacchetti in due gruppi in base alle loro caratteristiche:

  1. Librerie UI (stile-componenti, material-ui)
  2. Pacchetti core (reagire, reagire-dom)

Se sei un buon osservatore might potresti aver capito la differenza tra questi due gruppi.

Le librerie UI hanno una cartella dist che ha come destinazione la versione raggruppata e minimizzata per i sistemi di moduli ES e UMD / CJS. C'è una cartella lib che ha la versione transpiled della libreria.

I pacchetti core hanno solo una cartella che ha come destinazione la versione raggruppata e ridotta per il sistema di moduli CJS o UMD.

Ma perché c'è una differenza nell'output di compilazione di librerie UI e pacchetti core?

Librerie UI

Immagina di pubblicare la versione in bundle della nostra libreria e di ospitarla su CDN. Il nostro consumatore lo utilizzerà direttamente in un tag