Come sfruttare le macchine: essere produttivi con i task Runner
Pubblicato: 2022-03-10I task runner sono gli eroi (o i cattivi, a seconda del tuo punto di vista) che lavorano silenziosamente dietro la maggior parte delle applicazioni web e mobili. I task runner forniscono valore attraverso l'automazione di numerose attività di sviluppo come la concatenazione di file, l'avvio di server di sviluppo e la compilazione di codice. In questo articolo tratteremo gli script Grunt, Gulp, Webpack e npm. Forniremo anche alcuni esempi di ciascuno per iniziare. Verso la fine, lancerò alcune vittorie facili e suggerimenti per integrare le idee di questo post nella tua applicazione.
C'è la sensazione che i task runner e i progressi di JavaScript in generale stiano complicando eccessivamente il panorama del front-end. Sono d'accordo sul fatto che passare l'intera giornata a modificare gli script di build non è sempre il miglior uso del tuo tempo, ma i task runner hanno alcuni vantaggi se usati correttamente e con moderazione. Questo è il nostro obiettivo in questo articolo, coprire rapidamente le basi dei task runner più popolari e fornire esempi concreti per dare il via alla tua immaginazione su come questi strumenti possono adattarsi al tuo flusso di lavoro.
Ulteriori letture su SmashingMag:
- Diventa un utente esperto della riga di comando con Oh-My-ZSH e Z
- Un'introduzione a PostCSS
- Alzati e corri con Grunt
- Costruire Con Gulp
Una nota sulla riga di comando
I task runner e gli strumenti di compilazione sono principalmente strumenti da riga di comando. In questo articolo presumo un discreto livello di esperienza e competenza nel lavorare con la riga di comando. Se capisci come usare comandi comuni come cd
, ls
, cp
e mv
, allora dovresti essere a posto mentre esaminiamo i vari esempi. Se non ti senti a tuo agio nell'usare questi comandi, un ottimo post introduttivo è disponibile su Smashing Magazine. Diamo il via alle cose con il nonno di tutti loro: Grunt.
Grugnito
Grunt è stato il primo popolare task runner basato su JavaScript. Uso Grunt in qualche forma dal 2012. L'idea di base alla base di Grunt è di utilizzare uno speciale file JavaScript, Gruntfile.js
, per configurare vari plugin per eseguire attività. Ha un vasto ecosistema di plugin ed è uno strumento molto maturo e stabile. Grunt ha una fantastica directory web che indicizza la maggior parte dei plugin (circa 5.500 attualmente). Il genio semplice di Grunt è la sua combinazione di JavaScript e l'idea di un file di configurazione comune (come un makefile), che ha permesso a molti più sviluppatori di contribuire e utilizzare Grunt nei loro progetti. Significa anche che Grunt può essere posizionato sotto lo stesso sistema di controllo della versione del resto del progetto.
Grunt è testato in battaglia e stabile. Intorno al momento in cui scrivo, è stata rilasciata la versione 1.0.0, che è un enorme risultato per il team di Grunt. Poiché Grunt configura in gran parte vari plugin per lavorare insieme, può ingarbugliarsi (cioè disordinato e confuso da modificare) abbastanza rapidamente. Tuttavia, con un po' di cura e organizzazione (scomponendo le attività in file logici!), puoi farlo fare miracoli per qualsiasi progetto.
Nel raro caso in cui un plugin non sia disponibile per svolgere l'attività di cui hai bisogno, Grunt fornisce documentazione su come scrivere il tuo plugin. Tutto ciò che devi sapere per creare il tuo plug-in è JavaScript e l'API Grunt. Non dovrai quasi mai creare il tuo plug-in, quindi diamo un'occhiata a come utilizzare Grunt con un plug-in piuttosto popolare e utile!
Un esempio

Diamo un'occhiata a come funziona effettivamente Grunt. L'esecuzione di grunt
nella riga di comando attiverà il programma della riga di comando Grunt che cerca Gruntfile.js
nella radice della directory. Gruntfile.js
contiene la configurazione che controlla cosa farà Grunt. In questo senso, Gruntfile.js
può essere visto come una specie di libro di cucina che il cuoco (cioè Grunt, il programma) segue; e, come ogni buon libro di cucina, Gruntfile.js
conterrà molte ricette (es. attività).
Metteremo alla prova Grunt utilizzando il plug-in Grunticon per generare icone per un'ipotetica app Web. Grunticon prende in una directory di SVG e sputa diverse risorse:
- un file CSS con la codifica SVG in base-64 come immagini di sfondo;
- un file CSS con versioni PNG degli SVG con codifica base-64 come immagini di sfondo;
- un file CSS che fa riferimento a un singolo file PNG per ogni icona.
I tre diversi file rappresentano le varie funzionalità dei browser e dei dispositivi mobili. I dispositivi moderni riceveranno gli SVG ad alta risoluzione come un'unica richiesta (cioè un singolo file CSS). I browser che non gestiscono SVG ma gestiscono risorse con codifica base 64 riceveranno il foglio di stile PNG base 64. Infine, tutti i browser che non sono in grado di gestire questi due scenari otterranno il foglio di stile "tradizionale" che fa riferimento ai PNG. Tutto questo da un'unica directory di SVG!
La configurazione di questa attività è simile a questa:
module.exports = function(grunt) { grunt.config("grunticon", { icons: { files: [ { expand: true, cwd: 'grunticon/source', src: ["*.svg", ".png"], dest: 'dist/grunticon' } ], options: [ { colors: { "blue": "blue" } } ] } }); grunt.loadNpmTasks('grunt-grunticon'); };
Esaminiamo qui i vari passaggi:
- Devi avere installato Grunt a livello globale.
- Crea il file
Gruntfile.js
nella radice del progetto. È meglio installare anche Grunt come dipendenza npm nel filepackage.json
insieme a Grunticon tramitenpm i grunt grunt-grunticon --save-dev
. - Crea una directory di SVG e una directory di destinazione (dove andranno le risorse costruite).
- Inserisci un piccolo script
head
del tuo HTML, che determinerà quali icone caricare.
Ecco come dovrebbe apparire la tua directory prima di eseguire l'attività Grunticon:
|-- Gruntfile.js |-- grunticon | `-- source | `-- logo.svg `-- package.json
|-- Gruntfile.js |-- grunticon | `-- source | `-- logo.svg `-- package.json
Una volta che queste cose sono state installate e create, puoi copiare lo snippet di codice sopra in Gruntfile.js
. Dovresti quindi essere in grado di eseguire grunt grunticon
dalla riga di comando e vedere l'esecuzione dell'attività.
Lo snippet sopra fa alcune cose:
- aggiunge un nuovo oggetto di
config
a Grunt sulla riga 32 chiamatogrunticon
; - compila le varie opzioni e parametri per Grunticon nell'oggetto
icons
; - infine, estrae il plug-in Grunticon tramite
loadNPMTasks
.
Ecco come dovrebbe apparire la tua directory dopo Grunticon:
|-- Gruntfile.js |-- dist | `-- grunticon | |-- grunticon.loader.js | |-- icons.data.png.css | |-- icons.data.svg.css | |-- icons.fallback.css | |-- png | | `-- logo.png | `-- preview.html |-- grunticon | `-- source | `-- logo.svg `-- package.json
|-- Gruntfile.js |-- dist | `-- grunticon | |-- grunticon.loader.js | |-- icons.data.png.css | |-- icons.data.svg.css | |-- icons.fallback.css | |-- png | | `-- logo.png | `-- preview.html |-- grunticon | `-- source | `-- logo.svg `-- package.json
Ecco fatto: finito! In poche righe di configurazione e un paio di installazioni di pacchetti, abbiamo automatizzato la generazione delle nostre risorse di icone! Si spera che questo inizi a illustrare il potere dei task runner: affidabilità, efficienza e portabilità.
Gulp: blocchi LEGO per il tuo sistema di costruzione
Gulp è emerso qualche tempo dopo Grunt e aspirava a essere uno strumento di compilazione che non fosse solo configurazione ma codice reale. L'idea alla base del codice sulla configurazione è che il codice è molto più espressivo e flessibile rispetto alla modifica di infiniti file di configurazione. L'ostacolo con Gulp è che richiede più conoscenze tecniche di Grunt. Dovrai avere familiarità con l'API di streaming Node.js ed essere a tuo agio nello scrivere JavaScript di base.
L'uso da parte di Gulp dei flussi Node.js è il motivo principale per cui è più veloce di Grunt. L'uso dei flussi significa che, invece di utilizzare il file system come "database" per le trasformazioni dei file, Gulp utilizza le trasformazioni in memoria. Per ulteriori informazioni sugli stream, consulta la documentazione dell'API degli stream Node.js, insieme al manuale dello stream.
Un esempio

Come nella sezione Grunt, metteremo alla prova Gulp con un semplice esempio: concatenare i nostri moduli JavaScript in un unico file dell'app.
L'esecuzione di Gulp è come l'esecuzione di Grunt. Il programma da riga di comando gulp
cercherà il ricettario delle ricette (ad esempio Gulpfile.js
) nella directory in cui è eseguito.
Limitare il numero di richieste effettuate da ciascuna pagina è considerata una best practice per le prestazioni web (soprattutto sui dispositivi mobili). Tuttavia, collaborare con altri sviluppatori è molto più semplice se la funzionalità è suddivisa in più file. Inserisci i task runner. Possiamo usare Gulp per combinare più file di JavaScript per la nostra applicazione in modo che i client mobili debbano caricare un singolo file, invece di molti.
Gulp ha lo stesso enorme ecosistema di plugin di Grunt. Quindi, per semplificare questo compito, faremo affidamento sul plug-in gulp-concat. Diciamo che la struttura del nostro progetto si presenta così:
|-- dist | `-- app.js |-- gulpfile.js |-- package.json `-- src |-- bar.js `-- foo.js
Due file JavaScript si trovano nella nostra directory src
e vogliamo combinarli in un unico file, app.js
, nella nostra directory dist/
. Possiamo usare il seguente compito Gulp per raggiungere questo obiettivo.
var gulp = require('gulp'); var concat = require('gulp-concat'); gulp.task('default', function() { return gulp.src('./src/*.js') .pipe(concat('app.js')) .pipe(gulp.dest('./dist/')); });
I bit importanti sono nel callback di gulp.task
. Lì, utilizziamo l'API gulp.src
per ottenere tutti i file che terminano con .js
nella nostra directory src
. L'API gulp.src
restituisce un flusso di quei file, che possiamo quindi passare (tramite l'API pipe
) al plug-in gulp-concat. Il plugin quindi concatena tutti i file nel flusso e lo passa alla funzione gulp.dest
. La funzione gulp-dest
scrive semplicemente l'input che riceve su disco.
Puoi vedere come Gulp utilizza i flussi per fornirci "mattoni" o "catene" per le nostre attività. Un tipico flusso di lavoro di Gulp è simile al seguente:
- Ottieni tutti i file di un certo tipo.
- Passa quei file a un plug-in (concat!) o esegui una trasformazione.
- Passa quei file trasformati a un altro blocco (nel nostro caso, il blocco
dest
, che termina la nostra catena).
Come nell'esempio Grunt, eseguire semplicemente gulp
dalla radice della directory del nostro progetto attiverà l'attività default
definita nel file Gulpfile.js
. Questa attività concatena i nostri file e proseguiamo con lo sviluppo della nostra app o del nostro sito Web.
Pacchetto Web
L'ultima aggiunta al task runner club JavaScript è Webpack. Webpack si autodefinisce un "aggregatore di moduli", il che significa che può creare dinamicamente un pacchetto di codice JavaScript da più file separati utilizzando modelli di moduli come il modello CommonJS. Webpack ha anche plugin, che chiama caricatori.
Webpack è ancora abbastanza giovane e ha una documentazione piuttosto densa e confusa. Pertanto, consiglierei il repository Webpack di Pete Hunt come ottimo punto di partenza prima di immergerti nella documentazione ufficiale. Inoltre, non consiglierei Webpack se non conosci i task runner o non ti senti esperto in JavaScript. A parte questi problemi, è ancora uno strumento più specifico rispetto all'ampiezza generale di Grunt e Gulp. Molte persone usano Webpack insieme a Grunt o Gulp proprio per questo motivo, consentendo a Webpack di eccellere nel raggruppare i moduli e lasciando che Grunt o Gulp gestiscano attività più generiche.

Webpack alla fine ci consente di scrivere codice in stile Node.js per il browser, una grande vittoria per la produttività e una netta separazione delle preoccupazioni nel nostro codice tramite moduli. Usiamo Webpack per ottenere lo stesso risultato ottenuto con l'esempio di Gulp, combinando più file JavaScript in un unico file dell'app.
Un esempio

Webpack viene spesso utilizzato con Babel per trasferire il codice ES6 in ES5. Il trasferimento del codice da ES6 a ES5 consente agli sviluppatori di utilizzare lo standard emergente ES6 mentre serve ES5 a browser o ambienti che non supportano ancora completamente ES6. Tuttavia, in questo esempio, ci concentreremo sulla creazione di un semplice pacchetto dei nostri due file dall'esempio Gulp. Per iniziare, dobbiamo installare Webpack e creare un file di configurazione, webpack.config.js
. Ecco come appare il nostro file:
module.exports = { entry: "./src/foo.js", output: { filename: "app.js", path: "./dist" } };
In questo esempio, stiamo puntando Webpack al nostro file src/foo.js
per iniziare il suo lavoro di esplorazione del nostro grafico delle dipendenze. Abbiamo anche aggiornato il nostro file foo.js
in modo che assomigli a questo:
//foo.js var bar = require("./bar"); var foo = function() { console.log('foo'); bar(); }; module.exports = foo;
E abbiamo aggiornato il nostro file bar.js
in modo che assomigli a questo:
//bar.js var bar = function() { console.log('bar'); }; module.exports = bar;
Questo è un esempio CommonJS molto semplice. Noterai che questi file ora "esportano" una funzione. In sostanza, CommonJS e Webpack ci consentono di iniziare a organizzare il nostro codice in moduli autonomi che possono essere importati ed esportati in tutta la nostra applicazione. Webpack è abbastanza intelligente da seguire le parole chiave di importazione ed esportazione e raggruppare tutto in un unico file, dist/app.js
. Non abbiamo più bisogno di mantenere un'attività di concatenazione e dobbiamo semplicemente aderire a una struttura per il nostro codice. Molto meglio!
Estendere
Webpack è simile a Gulp in quanto "È solo JavaScript". Può essere esteso per svolgere altre attività di task runner tramite il suo sistema di caricamento. Ad esempio, puoi usare css-loader e sass-loader per compilare Sass in CSS e persino usare Sass nel tuo JavaScript sovraccaricando il pattern CommonJS require
! Tuttavia, in genere sostengo l'utilizzo di Webpack esclusivamente per creare moduli JavaScript e l'utilizzo di un altro approccio più generico per l'esecuzione delle attività (ad esempio, Webpack e script npm o Webpack e Gulp per gestire tutto il resto).
Script npm
Gli script npm sono l'ultima mania hipster, e per una buona ragione. Come abbiamo visto con tutti questi strumenti, il numero di dipendenze che potrebbero introdurre in un progetto potrebbe finire fuori controllo. Il primo post che ho visto a sostegno degli script npm come punto di ingresso per un processo di compilazione è stato di James Halliday. Il suo post riassume perfettamente il potere ignorato degli script npm (enfasi mia):
Ci sono alcuni strumenti fantasiosi per eseguire l'automazione della compilazione su progetti JavaScript di cui non ho mai sentito il fascino perché il comando npm run
meno noto è stato perfettamente adeguato per tutto ciò che dovevo fare pur mantenendo un footprint di configurazione molto piccolo .
Hai catturato l'ultimo pezzo alla fine? L'attrattiva principale degli script npm è che hanno un "impronta di configurazione molto piccola". Questo è uno dei motivi principali per cui gli script npm hanno iniziato a prendere piede (quasi quattro anni dopo, purtroppo). Con Grunt, Gulp e persino Webpack, alla fine si inizia ad affogare nei plugin che avvolgono i binari e raddoppiano il numero di dipendenze in un progetto.
Keith Cirkel ha il tutorial di riferimento sull'utilizzo di npm per sostituire Grunt o Gulp. Fornisce il progetto su come sfruttare appieno la potenza degli script npm e ha introdotto un plug-in essenziale, Parallel Shell (e una miriade di altri simili).
Un esempio
Nella nostra sezione su Grunt, abbiamo preso il popolare modulo Grunticon e creato icone SVG (con PNG fallback) in un'attività Grunt. Questo era l'unico punto dolente con gli script npm per me. Per un po', manterrei Grunt installato per i progetti solo per usare Grunticon. Vorrei letteralmente "sborsare" Grunt nel mio compito npm per ottenere l'inizio del task-runner (o, come abbiamo iniziato a chiamarlo al lavoro, uno strumento di build turducken). Per fortuna, The Filament Group, il fantastico gruppo dietro Grunticon, ha rilasciato una versione standalone (cioè senza Grunt) del loro strumento, Grunticon-Lib. Quindi, usiamolo per creare alcune icone con gli script npm!
Questo esempio è un po' più avanzato di una tipica attività di script npm. Una tipica attività di script npm è una chiamata a uno strumento da riga di comando, con i flag o il file di configurazione appropriati. Ecco un'attività più tipica che compila il nostro Sass in CSS:
"sass": "node-sass src/scss/ -o dist/css",
Vedi come è solo una riga con varie opzioni? Nessun file di attività necessario, nessuno strumento di compilazione da avviare: basta npm run sass
dalla riga di comando e ora Sass è CSS. Una caratteristica davvero interessante degli script npm è il modo in cui è possibile concatenare le attività di script insieme. Ad esempio, supponiamo di voler eseguire un'attività prima dell'esecuzione dell'attività Sass. Creeremo una nuova voce di script come questa:
"presass": "echo 'before sass',
Esatto: pre-
comprende il prefisso. Comprende anche il prefisso post-
. Qualsiasi voce di script con lo stesso nome di un'altra voce di script con un pre-
o un post-
verrà eseguita prima o dopo quella voce.
La conversione delle nostre icone richiederà un vero file Node.js. Non è troppo grave, però. Basta creare una directory delle tasks
e creare un nuovo file chiamato grunticon.js
o icons.js
o qualsiasi altra cosa abbia senso per coloro che lavorano al progetto. Una volta creato il file, possiamo scrivere del JavaScript per attivare il nostro processo Grunticon.
Nota: tutti questi esempi usano ES6, quindi useremo babel-node per eseguire la nostra attività. Puoi facilmente usare ES5 e Node.js, se è più comodo.
import icons from "grunticon-lib"; import globby from "globby"; let files = globby.sync('src/icons/*'); let options = { colors: { "blue": "blue" } }; let icon = new icons(files, 'dist/icons', options); icon.process();
Entriamo nel codice e scopriamo cosa sta succedendo.
-
import
(cioè richiediamo) due librerie,grunticon-lib
eglobby
. Globby è uno dei miei strumenti preferiti e rende così facile lavorare con file e glob. Globby migliora Node.js Glob (seleziona tutti i file JavaScript tramite./*.js
) con il supporto Promise. In questo caso, lo stiamo usando per ottenere tutti i file nella directorysrc/icons
. - Una volta fatto ciò, impostiamo alcune opzioni in un oggetto
options
e quindi chiamiamo Grunticon-Lib con tre argomenti:- i file delle icone,
- la destinazione,
- le opzioni. La libreria prende il sopravvento e rosicchia quelle icone e alla fine crea le versioni SVG e PNG nella directory che vogliamo.
- Abbiamo quasi finito. Ricorda che questo è in un file separato e dobbiamo aggiungere un "hook" per chiamare questo file dal nostro script npm, come questo:
"icons": "babel-node tasks/icons.js"
. - Ora possiamo eseguire
npm run icons
e le nostre icone verranno create ogni volta.
Gli script npm offrono un livello di potenza e flessibilità simile a quello di altri task runner, senza il debito del plug-in.
Ripartizione dei task Runner coperti qui
Attrezzo | Professionisti | contro |
---|---|---|
Grugnito | Non è necessaria alcuna conoscenza di programmazione reale | Il più dettagliato dei task runner trattati qui |
Sorso | Configura le attività con JavaScript e stream effettivi | Richiede la conoscenza di JavaScript |
Aggiunge codice a un progetto (potenzialmente più bug) | ||
Pacchetto Web | Il migliore della classe nel raggruppamento di moduli | Più difficile per compiti più generici (ad esempio, da Sass a CSS) |
script npm | Interazione diretta con gli strumenti della riga di comando. | Alcune attività non sono possibili senza un task runner. |
Alcune vittorie facili
Tutti questi esempi e i task runner potrebbero sembrare travolgenti, quindi analizziamolo. Innanzitutto, spero che tu non tolga da questo articolo che qualsiasi task runner o sistema di build che stai attualmente utilizzando deve essere immediatamente sostituito con uno menzionato qui. La sostituzione di sistemi importanti come questo non dovrebbe essere fatta senza molta considerazione. Ecco il mio consiglio per aggiornare un sistema esistente: fallo in modo incrementale.
Script wrapper!
Un approccio incrementale consiste nell'esaminare la scrittura di alcuni script npm "wrapper" attorno ai task runner esistenti per fornire un vocabolario comune per i passaggi di compilazione che è al di fuori del task runner effettivo utilizzato. Uno script wrapper potrebbe essere semplice come questo:
{ "scripts": { "start": "gulp" } }
Molti progetti utilizzano i blocchi di script npm start
e test
per aiutare i nuovi sviluppatori ad acclimatarsi rapidamente. Uno script wrapper introduce un altro livello di astrazione nella catena di build del tuo task runner, ma penso che valga la pena essere in grado di standardizzare le primitive npm (ad esempio test
). I comandi npm hanno una longevità migliore rispetto a un singolo strumento.
Cospargere con un piccolo Webpack
Se tu o il tuo team state sentendo il dolore di mantenere un fragile "ordine in bundle" per il vostro JavaScript, o state cercando di eseguire l'aggiornamento a ES6, considerate questa un'opportunità per introdurre Webpack nel vostro sistema di esecuzione delle attività esistente. Webpack è fantastico in quanto puoi usarne quanto vuoi e comunque trarne valore. Inizia semplicemente facendo raggruppare il codice dell'applicazione, quindi aggiungi babel-loader al mix. Webpack ha una tale profondità di funzionalità che sarà in grado di ospitare praticamente qualsiasi aggiunta o nuova funzionalità per un po' di tempo.
Usa facilmente PostCSS con gli script npm
PostCSS è un'ottima raccolta di plugin che trasformano e migliorano i CSS una volta scritti e preelaborati. In altre parole, è un post-processore. È abbastanza facile sfruttare PostCSS usando gli script npm. Supponiamo di avere uno script Sass come nel nostro esempio precedente:
"sass": "node-sass src/scss/ -o dist/css",
Possiamo utilizzare le parole chiave del lifecycle
di vita dello script npm per aggiungere uno script da eseguire automaticamente dopo l'attività Sass:
"postsass": "postcss --use autoprefixer -c postcss.config.json dist/css/*.css -d dist/css",
Questo script verrà eseguito ogni volta che viene eseguito lo script Sass. Il pacchetto postcss-cli è ottimo, perché puoi specificare la configurazione in un file separato. Si noti che in questo esempio, aggiungiamo un'altra voce di script per eseguire una nuova attività; questo è un modello comune quando si utilizzano gli script npm. Puoi creare un flusso di lavoro che svolga tutte le varie attività necessarie alla tua app.
Conclusione
I task runner possono risolvere problemi reali. Ho utilizzato i task runner per compilare diverse build di un'applicazione JavaScript, a seconda che l'obiettivo fosse la produzione o lo sviluppo locale. Ho anche utilizzato i task runner per compilare modelli di Handlebars, per distribuire un sito Web alla produzione e per aggiungere automaticamente i prefissi dei fornitori che mancano nel mio Sass. Questi non sono compiti banali, ma una volta che sono stati avvolti in un task runner, sono diventati facili.
I task runner sono in continua evoluzione e cambiamento. Ho cercato di coprire quelli più usati nell'attuale zeitgeist. Tuttavia, ce ne sono altri che non ho nemmeno menzionato, come Broccoli, Brunch e Arpa. Ricorda che questi sono solo strumenti: usali solo se risolvono un problema particolare, non perché tutti gli altri li usano. Buon compito in esecuzione!