Articles

Impostare un progetto ES6 usando Babel e webpack

In questo articolo, vedremo come creare una configurazione di build per gestire il JavaScript moderno (in esecuzione nei browser) usando Babel e webpack.

Questo è necessario per assicurare che il nostro codice JavaScript moderno in particolare sia reso compatibile con una gamma più ampia di browser di quanto potrebbe essere altrimenti.

JavaScript, come molte tecnologie legate al web, è in continua evoluzione. Ai bei tempi, potevamo inserire un paio di tag <script> in una pagina, magari includere jQuery e un paio di plugin, e poi essere pronti a partire.

Tuttavia, dall’introduzione di ES6, le cose sono diventate progressivamente più complicate. Il supporto dei browser per le nuove caratteristiche del linguaggio è spesso discontinuo, e poiché le applicazioni JavaScript diventano più ambiziose, gli sviluppatori stanno iniziando ad usare i moduli per organizzare il loro codice. A sua volta, questo significa che se state scrivendo JavaScript moderno oggi, avrete bisogno di introdurre una fase di compilazione nel vostro processo.

Come potete vedere dai link sottostanti, la conversione da ES6 a ES5 aumenta drasticamente il numero di browser che possiamo supportare.

  • Compatibilità ES6
  • Compatibilità ES5

Lo scopo di un sistema di build è quello di automatizzare il flusso di lavoro necessario per ottenere il nostro codice pronto per i browser e la produzione. Questo può includere passi come la trasposizione del codice a uno standard diverso, la compilazione di Sass in CSS, il bundling dei file, la minificazione e la compressione del codice e molti altri. Per assicurarsi che questi siano ripetibili in modo coerente, è necessario un sistema di compilazione per avviare i passi in una sequenza nota da un singolo comando.

Prequisiti

Per poter seguire, è necessario avere installato sia Node.js che npm (sono forniti insieme). Raccomando di usare un gestore di versioni come nvm per gestire la tua installazione di Node (ecco come fare), e se vuoi un po’ di aiuto per imparare ad usare npm, dai un’occhiata al tutorial per principianti di SitePoint su npm.

Impostazioni

Crea una cartella principale da qualche parte sul tuo computer e navigaci dentro dal tuo terminale/linea di comando. Questa sarà la tua cartella <ROOT>.

Crea un file package.json con questo:

npm init -y

Nota: Il flag -y crea il file con impostazioni predefinite, e significa che non hai bisogno di completare nessuno dei soliti dettagli dalla linea di comando. Possono essere cambiati nel tuo editor di codice in seguito, se lo desideri.

Nella tua cartella <ROOT>, crea le cartelle src, src/js e public. La cartella src/js sarà dove metteremo il nostro codice sorgente non processato, e la cartella public sarà dove finirà il codice transpilato.

Traspilare con Babel

Per iniziare, installeremo babel-cli, che fornisce la possibilità di trasporre ES6 in ES5, e babel-preset-env, che ci permette di indirizzare specifiche versioni del browser con il codice trasposto.

npm install babel-cli babel-preset-env --save-dev

Ora dovresti vedere quanto segue nel tuo package.json:

"devDependencies": { "babel-cli": "^6.26.0", "babel-preset-env": "^1.6.1"}

Mentre siamo nel file package.json, cambiamo la sezione scripts in questo modo:

"scripts": { "build": "babel src -d public"},

Questo ci dà la possibilità di chiamare Babel tramite uno script, piuttosto che direttamente dal terminale ogni volta. Se vuoi saperne di più sugli script npm e su cosa possono fare, dai un’occhiata a questo tutorial di SitePoint.

Infine, prima di poter testare se Babel fa il suo dovere, dobbiamo creare un file di configurazione .babelrc. Questo è quello a cui il nostro pacchetto babel-preset-env farà riferimento per i suoi parametri di trasposizione.

Crea un nuovo file nella tua directory <ROOT> chiamato .babelrc e incollaci dentro il seguente:

{ "presets": } } ] ]}

Questo imposterà Babel per la trasposizione per le ultime due versioni di ogni browser, più Safari alla v7 o superiore. Altre opzioni sono disponibili a seconda dei browser che devi supportare.

Con questo salvato, possiamo ora testare le cose con un file JavaScript di esempio che usa ES6. Per gli scopi di questo articolo, ho modificato una copia di leftpad per usare la sintassi ES6 in un certo numero di posti: letterali template, funzioni freccia, const e let.

Salva questo come src/js/leftpad.js e dal tuo terminale esegui quanto segue:

npm run build

Se tutto è come previsto, nella tua cartella public dovresti ora trovare un nuovo file chiamato js/leftpad.js. Se lo apri, scoprirai che non contiene più alcuna sintassi ES6 e assomiglia a questo:

Organizzare il tuo codice con i moduli ES6

Un modulo ES6 è un file JavaScript contenente funzioni, oggetti o valori primitivi che vuoi rendere disponibili ad un altro file JavaScript. Si exportda uno, e import nell’altro. Ogni serio progetto JavaScript moderno dovrebbe considerare l’uso di moduli. Vi permettono di spezzare il vostro codice in unità autonome e quindi rendono le cose più facili da mantenere; vi aiutano ad evitare l’inquinamento dei namespace; e aiutano a rendere il vostro codice più portabile e riutilizzabile.

Mentre la maggior parte della sintassi ES6 è ampiamente disponibile nei browser moderni, questo non è ancora il caso dei moduli. Al momento in cui scrivo, sono disponibili in Chrome, Safari (inclusa l’ultima versione per iOS) e Edge; sono nascosti dietro una bandiera in Firefox e Opera; e non sono disponibili (e probabilmente non lo saranno mai) in IE11, né nella maggior parte dei dispositivi mobili.

Nella prossima sezione, vedremo come possiamo integrare i moduli nella nostra configurazione di build.

Export

La parola chiave export è ciò che ci permette di rendere i nostri moduli ES6 disponibili in altri file, e ci dà due opzioni per farlo – named e default. Con l’esportazione con nome, si possono avere più esportazioni per modulo, mentre con l’esportazione di default se ne ha solo una per modulo. Le esportazioni con nome sono particolarmente utili quando hai bisogno di esportare diversi valori. Per esempio, potreste avere un modulo che contiene una serie di funzioni di utilità che devono essere rese disponibili in vari posti all’interno delle vostre applicazioni.

Così trasformiamo il nostro file leftPad in un modulo, che possiamo poi richiedere in un secondo file.

Esportazione con nome

Per creare un’esportazione con nome, aggiungiamo quanto segue alla fine del file leftPad:

export { leftPad };

Possiamo anche rimuovere la dichiarazione "use strict"; dall’inizio del file, poiché i moduli vengono eseguiti in modalità strict per impostazione predefinita.

Esportazione fallita

Come c’è solo una singola funzione da esportare nel file leftPad, potrebbe essere un buon candidato per usare invece export default:

export default function leftPad(str, len, ch) { ...}

Ancora una volta, si può rimuovere la dichiarazione "use strict"; dalla cima del file.

Importa

Per fare uso dei moduli esportati, ora abbiamo bisogno di importarli nel file (modulo) in cui vogliamo usarli.

Per l’opzione export default, il modulo esportato può essere importato con qualsiasi nome si voglia scegliere. Per esempio, il modulo leftPad può essere importato così:

import leftPad from './leftpad';

Oppure potrebbe essere importato con un altro nome, così:

import pineapple_fritter from './leftpad';

Funzionalmente, entrambi funzioneranno esattamente allo stesso modo, ma ovviamente ha senso usare o lo stesso nome con cui è stato esportato, o qualcosa che renda l’importazione comprensibile – forse dove il nome esportato si scontrerebbe con un altro nome di variabile che già esiste nel modulo ricevente.

Per l’opzione di esportazione con nome, dobbiamo importare il modulo usando lo stesso nome con cui è stato esportato. Per il nostro modulo di esempio, lo importeremo in un modo simile a quello che abbiamo usato con la sintassi export default, ma in questo caso, dobbiamo avvolgere il nome importato con delle parentesi graffe:

import { leftPad } from './leftpad';

Le parentesi graffe sono obbligatorie con un’esportazione con nome, e fallirà se non sono usate.

È possibile cambiare il nome di un’esportazione con nome all’importazione se necessario, e per farlo, abbiamo bisogno di modificare leggermente la nostra sintassi usando una sintassi import as . Come con export, c’è una varietà di modi per farlo, tutti dettagliati nella pagina MDN sull’importazione.

import { leftPad as pineapple_fritter } from './leftpad_es6';

Ancora una volta, il cambio di nome è un po’ senza senso, ma illustra il punto che possono essere cambiati in qualsiasi cosa. Dovreste sempre attenervi alle buone pratiche di denominazione, a meno che, naturalmente, non stiate scrivendo routine per preparare ricette a base di frutta.

Consumo del modulo esportato

Per fare uso del modulo esportato leftPad, ho creato il seguente file index.js nella cartella src/js. Qui, faccio un ciclo attraverso un array di numeri seriali e li precedo con degli zeri per trasformarli in una stringa di otto caratteri. Più avanti, ne faremo uso e li invieremo in un elemento di lista ordinata su una pagina HTML. Nota che questo esempio usa la sintassi di esportazione predefinita:

Come abbiamo fatto prima, esegui lo script di compilazione dalla directory <ROOT>:

npm run build

Babel ora creerà un file index.js nella directory public/js. Come per il nostro file leftPad.js, dovresti vedere che Babel ha sostituito tutta la sintassi ES6 e ha lasciato solo la sintassi ES5. Potreste anche notare che ha convertito la sintassi del modulo ES6 nella sintassi module.exports basata sul Nodo, il che significa che possiamo eseguirlo dalla linea di comando:

node public/js/index.js// 

Il vostro terminale dovrebbe ora registrare un array di stringhe precedute da zeri per renderle tutte lunghe otto caratteri. Fatto questo, è il momento di dare un’occhiata a webpack.

Introduzione a webpack e integrazione con Babel

Come detto, i moduli ES6 permettono allo sviluppatore JavaScript di spezzare il proprio codice in pezzi gestibili, ma la conseguenza di ciò è che questi pezzi devono essere serviti al browser richiedente, aggiungendo potenzialmente dozzine di richieste HTTP aggiuntive al server – qualcosa che dovremmo davvero cercare di evitare. È qui che entra in gioco webpack.

webpack è un aggregatore di moduli. Il suo scopo principale è quello di elaborare la vostra applicazione rintracciando tutte le sue dipendenze, quindi impacchettarle tutte in uno o più bundle che possono essere eseguiti nel browser. Tuttavia, può essere molto più di questo, a seconda di come è configurato.

La configurazione diwebpack si basa su quattro componenti chiave:

  • un punto di ingresso
  • una posizione di uscita
  • caricatori
  • plugins

Entry: Questo contiene il punto di partenza della tua applicazione da dove webpack può identificare le sue dipendenze.

Output: Questo specifica dove vorresti che il bundle elaborato fosse salvato.

Loader: Questi sono un modo per convertire una cosa come input e generare qualcos’altro come output. Possono essere usati per estendere le capacità di webpack di gestire più che semplici file JavaScript, e quindi convertire anche quelli in moduli validi.

Plugins: Questi sono usati per estendere le capacità di webpack in altri compiti oltre al bundling – come la minificazione, il linting e l’ottimizzazione.

Per installare webpack, esegui quanto segue dalla tua directory <ROOT>:

npm install webpack webpack-cli --save-dev

Questo installa webpack localmente nel progetto, e dà anche la possibilità di eseguire webpack dalla riga di comando attraverso l’aggiunta di webpack-cli. Ora dovresti vedere webpack elencato nel tuo file package.json. Mentre sei in quel file, modifica la sezione degli script come segue, in modo che ora sappia usare webpack invece di Babel direttamente:

"scripts": { "build": "webpack --config webpack.config.js"},

Come puoi vedere, questo script richiama un file webpack.config.js, quindi creiamolo nella nostra directory <ROOT> con il seguente contenuto:

Questo è più o meno il file di configurazione più semplice di cui hai bisogno con webpack. Puoi vedere che usa le sezioni di ingresso e di uscita descritte prima (potrebbe funzionare solo con queste), ma contiene anche un’impostazione mode: 'development'.

webpack ha la possibilità di usare le modalità “sviluppo” o “produzione”. L’impostazione mode: 'development' ottimizza la velocità di compilazione e il debug, mentre mode: 'production' ottimizza la velocità di esecuzione in fase di esecuzione e la dimensione del file di output. C’è una buona spiegazione delle modalità nell’articolo di Tobias Koppers “webpack 4: mode and optimization” se vuoi leggere di più su come possono essere configurate oltre le impostazioni predefinite.

Poi, rimuovi qualsiasi file dalla cartella public/js. Poi riesegui questo:

npm run build

Vedrai che ora contiene un solo file ./public/bundle.js. Aprite il nuovo file, però, e i due file con cui abbiamo iniziato sembrano piuttosto diversi. Questa è la sezione del file che contiene il codice index.js. Anche se è stato pesantemente modificato dal nostro originale, puoi ancora distinguere i suoi nomi di variabili:

Se esegui node public/js/bundle.js dalla cartella <ROOT>, vedrai che otterrai gli stessi risultati che abbiamo avuto in precedenza.

Transpiling

Come detto prima, i caricatori ci permettono di convertire una cosa in qualcos’altro. In questo caso, vogliamo convertire ES6 in ES5. Per farlo, abbiamo bisogno di un altro paio di pacchetti:

npm install babel-loader babel-core --save-dev

Per utilizzarli, il webpack.config.js ha bisogno di una sezione modulo da aggiungere dopo la sezione output, così:

Questo usa una dichiarazione regex per identificare i file JavaScript da transpilare con il babel-loader, mentre esclude qualsiasi cosa nella cartella node_modules. Infine, al babel-loader viene detto di usare il pacchetto babel-preset-env installato in precedenza, per stabilire i parametri di transpilazione impostati nel file .babelrc.

Fatto questo, puoi rieseguire questo:

npm run build

Poi controlla il nuovo public/js/bundle.js e vedrai che tutte le tracce della sintassi ES6 sono sparite, ma produce ancora lo stesso output di prima.

Portare tutto sul browser

Avendo costruito una configurazione webpack e Babel funzionante, è il momento di portare ciò che abbiamo fatto sul browser. È necessario un piccolo file HTML, che dovrebbe essere creato nella cartella <ROOT> come segue:

Per visualizzare la lista è necessario un po’ più di JavaScript, quindi modifichiamo ./src/js/index.js per farlo accadere:

Ora, se aprite index.html nel vostro browser, dovreste vedere apparire una lista ordinata, come questa:

Passando oltre

Come configurato sopra, il nostro sistema di build è praticamente pronto a partire. Ora possiamo usare webpack per fare il bundle dei nostri moduli e transpilare il codice ES6 in ES5 con Babel.

Tuttavia, è un po’ fastidioso che, per transpilare il nostro codice ES6, dobbiamo eseguire npm run build ogni volta che facciamo un cambiamento.

Aggiungere un ‘watch’

Per ovviare alla necessità di eseguire ripetutamente npm run build, puoi impostare un 'watch' sui tuoi file e fare in modo che webpack ricompili automaticamente ogni volta che vede un cambiamento in uno dei file nella cartella ./src. Per implementare ciò, modifica la sezione scripts del file package.json, come segue:

"scripts": { "watch": "webpack --watch", "build": "webpack --config webpack.config.js"},

Per verificare che funzioni, esegui npm run watch dal terminale, e vedrai che non torna più al prompt dei comandi. Ora tornate a src/js/index.js e aggiungete un valore extra nell’array serNos e salvate. Il mio ora assomiglia a questo:

const serNos = ;

Se ora controllate il terminale, vedrete che si è disconnesso, e che ha eseguito nuovamente il task webpack build. E tornando al browser e facendo il refresh, vedrete il nuovo valore aggiunto alla fine della lista, essendo stato processato con leftPad.

Rinfresca il browser automaticamente

Sarebbe davvero bello ora se potessimo fare in modo che webpack aggiorni il browser automaticamente ogni volta che facciamo un cambiamento. Facciamolo installando un pacchetto npm aggiuntivo chiamato webpack-dev-server. Non dimenticatevi però di togliere Ctrl + c dal task watch prima!

npm install webpack-dev-server --save-dev

Fatto questo, aggiungiamo un nuovo script al file package.json per chiamare il nuovo pacchetto. La sezione scripts dovrebbe ora contenere questo:

Nota il flag --open-page aggiunto alla fine dello script. Questo dice a webpack-dev-server di aprire una pagina specifica nel tuo browser predefinito usando la sua modalità iframe.

Esegui ora npm start e dovresti vedere una nuova scheda del browser aperta a con la lista delle parti visualizzata. Per mostrare che il 'watch' funziona, vai a src/js/index.js e aggiungi un altro nuovo valore alla fine dell’array serNos. Quando salvi le tue modifiche, dovresti notare che si riflettono quasi immediatamente nel browser.

Con questo completo, l’unica cosa che rimane è che la modalità in webpack.config.js sia impostata a production. Una volta impostato ciò, webpack minificherà anche il codice in uscita in ./public/js/bundle.js. Dovresti notare che se il mode non è impostato, webpack userà di default la configurazione production.

Conclusione

In questo articolo, hai visto come impostare un sistema di compilazione per JavaScript moderno. Inizialmente, questo ha usato Babel dalla riga di comando per convertire la sintassi ES6 in ES5. Avete poi visto come fare uso dei moduli ES6 con le parole chiave export e import, come integrare webpack per eseguire un compito di bundling, e come aggiungere un compito watch per automatizzare l’esecuzione di webpack ogni volta che vengono rilevate modifiche a un file sorgente. Infine avete visto come installare webpack-dev-server per aggiornare automaticamente la pagina ogni volta che viene fatta una modifica.

Se desiderate proseguire, vi suggerisco di leggere l’immersione profonda di SitePoint su webpack e il bundling dei moduli, così come la ricerca di ulteriori caricatori e plugin che permetteranno a webpack di gestire Sass e i compiti di compressione delle risorse. Guarda anche il eslint-loader e il plugin per Prettier.

Felice bundling …