Come passare da Gulp a Webpack: una guida per iniziare

Passare da Gulp a Webpack può essere traumatico: per molti sviluppatori Wepback è solo un altro oscuro tool Javascript.

Passare da Gulp a Webpack: webpack logo

Anch’io non ho prestato molta attenzione a Webpack sin dall’inizio. Quando però mi sono reso conto che avrei potuto fare le stesse cose che facevo con Gulp, ma con molto meno codice e salti mortali, ho deciso di dargli una possibilità.

In questo articolo scoprirai come.

Venendo da Gulp, che è un task runner per lo sviluppo web, i passaggi principali che mi interessava convertire a Webpack erano:

  1. generare una serie di pagine web, minimizzando il markup HTML
  2. ottimizzare le immagini
  3. compilare una serie di file SASS in un’unico file CSS, minimizzando il CSS in uscita
  4. aggiungere i vendor prefixes all’interno del CSS
  5. trasformare il codice Javascript con Babel (transpilation)
  6. compilare tutti gli assets automaticamente quando viene rilevata una modifica

Non ti preoccupare se la maggior parte di questi termini ti sono estranei. Anche se questo articolo non è rivolto ai principianti cercherò di introdurre comunque i concetti più importanti. Per tutti gli altri, esperti o meno, molti dei termini dovrebbero essere già familiari.

Pronti?

Passare da Gulp a Webpack: configurare il progetto

Inizia creando una directory per il progetto:

mkdir from-gulp-to-webpack

ed una directory per contenere i sorgenti:

cd from-gulp-to-webpack
mkdir -p src/{js,img,_scss}

Inizializza il progetto con:

npm init -y

ed installa Webpack con:

npm i webpack --save-dev

Apri il file package.json ed aggiungi un task per il build:

"scripts": {
  "build": "webpack"
},

poi salva e chiudi il file.

Pronti!

Passare da Gulp a Webpack: generare le pagine HTML e minimizzare il markup

Webpack è un module bundler. Ovvero: a partire da un file Javascript di ingresso, che importa altri file Javascript, Webpack è in grado di creare un albero delle dipendenze, includendo a ritroso tutto il codice che viene richiesto dal file di ingresso.

Ad esempio, se ho un file, app.js, che importa un altro file Javascript:

import "./contact_form";

Webpack è in grado di includere contact_form.js. E se quest’ultimo include a sua volta altri file Javascript, Webpack combina automaticamente anche tutti gli altri file. Il risultato finale sarà un unico file, app.js che contiene tutto il codice necessario per la nostra applicazione.

Essendo nato principalmente per Javascript, Webpack non è in grado di processare ed estrarre i file HTML: per questo motivo esistono dei loader, ovvero dei “trasformatori” che riconoscono il tipo di file e lo trasformano in base alle esigenze.

Per processare l’HTML, Webpack utilizza due componenti: html-webpack-plugin e html-loader.

Aggiungili al progetto con:

npm i html-webpack-plugin html-loader --save-dev

Per proseguire, Webpack ha bisogno anche di un file di configurazione. Nella directory del progetto crea il file webpack.config.js e configuralo come segue:

const HtmlWebPackPlugin = require("html-webpack-plugin");
const path = require("path");

module.exports = {
  entry: ["./src/js/index.js"],
  output: {
    filename: "js/index.js",
    path: path.join(__dirname, "./build/")
  },
  module: {
    rules: [
      {
        test: /\.html$/,
        use: [{ loader: "html-loader", options: { minimize: true } }]
      }
    ]
  },
  plugins: [
    new HtmlWebPackPlugin({
      template: "./src/index.html",
      filename: "./index.html"
    })
  ]
};

Nel file di configurazione Webpack i punti più importanti sono:

  1. entry, ovvero il file Javascript di ingresso che contiene il codice dell’applicazione
  2. output, ovvero il file di uscita che verrà generato da Webpack
  3. module and rules, ovvero la dichiarazione dei vari loader
  4. plugins, dove vengono dichiarati i plugin

Con questa configurazione siamo finalmente in grado di generare un file HTML, partendo da un template.

Crea un primo file html all’interno di ./src/index.html:

<!DOCTYPE html>
<html lang="it">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Webpack</title>
</head>

<body>
    <section class="main">
        <h1>Hello Webpack!!</h1>
    </section>
</body>

</html>

oltre ad un file Javascript, (minimale, a titolo di esempio), ./src/js/index.js:

console.log(`I've been required by Webpack`);

lancia il build con:

npm run build

ed attendi la compilazione (in genere richiede qualche secondo):

Passare da Gulp a Webpack: generare le pagine HTML e minimizzare il markup
Il processo di compilazione di Webpack, dove i file di ingresso vengono letti e trasformati

Ora se provi a guardare nella directory build vedrai che Webpack ha generato il file HTML, minimizzato.

E se provi ad aprire il file HTML nel browser, vedrai che tutto funziona come ti aspettavi:

Passare da Gulp a Webpack: il file HTML in uscita

Riguardo al codice Javascript, non è necessario includere manualmente lo script all’interno di un tag <script></script>, ci pensa Webpack.

Nella console del browser (aprila con F12) vedrai infatti il messaggio “I’ve been required by Webpack”.

OBIETTIVO 1 RAGGIUNTO: Webpack è in grado di generare e minimizzare un file HTML. Con Gulp, lo stesso risultato veniva ottenuto utilizzando gulp-htmlmin e molte più righe di codice

Passare da Gulp a Webpack: ottimizzare le immagini

Ottimizzare le immagini che compongono un sito web è fondamentale. Sempre più utenti utilizzano connessioni 3G e 4G per navigare. Non possiamo permetterci di servire un’immagine da 2MB su una connessione mobile.

Per ottimizzare le immagini con Gulp abbiamo a disposizione gulp-imagemin. Passando a Webpack invece le cose sono leggermente diverse: abbiamo bisogno di usare alcuni loader (ricorda, sono dei trasformatori).

I loader in questione sono: img-loader, url-loader, e file-loader, che devi aggiungere al progetto:

npm i img-loader url-loader file-loader --save-dev

Qui le cose si fanno interessanti perchè url-loader è in grado di trasformare le immagini PNG: se le immagini sono più piccole di N bytes, vengono trasformate automaticamente in URL con codifica Base64.

In altre parole, se un’immagine PNG è più piccola del limite impostato nel loader la stessa verrà inserita sotto forma di URL all’interno del file HTML.

Per chiarire, ecco come si presenta un’immagine PNG sotto forma di URL Base64:

Passare da Gulp a Webpack, ottimizzare le immagini
Un’immagine PNG molto piccola può essere inclusa direttamente all’interno del file HTML sotto forma di URL Base64

I vantaggi di questo approccio sono evidenti.

Per quanto riguarda img-loader invece, si tratta di un loader che ottimizza le immagini utilizzando imagemin.

Per testare la cosa aggiungi due immagini al file HTML che abbiamo creato prima, una di piccole dimensioni, e l’altra più grande (scarica due immagini PNG da internet e salvale nella directory img con i relativi filename):

<!DOCTYPE html>
<html lang="it">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Webpack</title>
</head>

<body>
    <section class="main">
        <h1>Hello Webpack!!</h1>
        <img src="img/big-image.png">
        <img src="img/small-image.png">
    </section>
</body>

</html>

poi modifica la configurazione di Webpack ed aggiungi i nuovi loader:

const HtmlWebPackPlugin = require("html-webpack-plugin");
const path = require("path");

module.exports = {
  entry: ["./src/js/index.js"],
  output: {
    filename: "js/index.js",
    path: path.join(__dirname, "./build/")
  },
  module: {
    rules: [
      {
        test: /\.html$/,
        use: [{ loader: "html-loader", options: { minimize: true } }]
      },
      {
        test: /\.(otf|eot|ttf|woff|svg|png|jpe?g)/i,
        use: [
          {
            loader: "url-loader",
            options: {
              name: "./img/[name].[ext]",
              limit: 10000
            }
          },
          {
            loader: "img-loader"
          }
        ]
      }
    ]
  },
  plugins: [
    new HtmlWebPackPlugin({
      template: "./src/index.html",
      filename: "./index.html"
    })
  ]
};

Ora lanciando il build con:

npm run build

succedono due cose:

  1. l’immagine più grande viene ottimizzata (ne viene ridotta la dimensione)
  2. l’immagine più piccola viene inclusa direttamente nel file HTML sotto forma di URL Base64
Passare da Gulp a Webpack: ottimizzare le immagini PNG
L’immagine più grande viene ottimizzata. L’immagine PNG più piccola viene inclusa come URL Base64

OBIETTIVO 2 RAGGIUNTO: Webpack è in grado di ottimizzare le immagini. In questo caso, passare da Gulp a Webpack porta con se il vantaggio di poter includere le immagini PNG più piccole direttamente all’interno del file HTML.

Passare da Gulp a Webpack: compilare da SASS a CSS, minimizzare gli stylesheets, aggiungere i vendor prefixes

Se HTML è lo scheletro di una pagina web, i CSS sono la parte creativa, la parte colorata. Una pagina web senza CSS avrebbe un aspetto poco invitante.

Dobbiamo includere il nostro CSS all’interno del file HTML che abbiamo creato prima, per dare alla pagina un’aspetto diverso (nell’esempio, non sarà necessariamente bello!).

Al giorno d’oggi è raro trovare qualcuno che scriva ancora in CSS puro. Linguaggi più avanzati come SASS e LESS hanno permesso di estendere i CSS portando diversi concetti quali le variabili, i mixins e gli import nel dominio dei fogli di style (stylesheets).

NOTA: Probabilmente l’utilità delle variabili SASS verrà meno con il passare del tempo, dopo il rilascio delle Custom Properties (variabili native per CSS)

Raramente un file SASS è distribuito in forma monolitica. Nella maggior parte dei casi esiste un main.scss che importa tutte le dipendenze, esempio:

@import "_utils/reset";
@import "_deps/vars";
@import "_deps/mixins";
@import "_base/base";
@import "_layout/grid";
@import "_animations/animations";
@import "_transitions/transitions";
@import "_regions/home-container";
@import "_regions/header";
@import "_regions/footer";

Inoltre i file SASS non possono essere interpretati dal browser così come sono: è necessario processarli e trasformarli in CSS.

Dobbiamo anche aggiungere i prefissi per i browser più vecchi: i prefissi CSS (vendor prefixes) sono necessari per garantire quanto più possibile la compatibilità dei nostri CSS da browser a browser.

Infine, come per l’HTML e le immagini, anche il CSS deve essere minimizzato per ridurne il peso, e così facendo anche il tempo di caricamento nel browser.

Con Gulp questi 3 passaggi richiedono la scrittura di 3 diversi tasks: 1 task per compilare da SASS a CSS; 1 task per aggiungere i prefissi; 1 task per minimizzare il CSS.

Con Webpack bastano solo poche righe di configurazione ed alcuni loader, che installiamo con:

npm i css-loader sass-loader postcss-loader node-sass extract-text-webpack-plugin --save-dev

modifica poi la configurazione di Webpack ed aggiungi i nuovi loader:

const HtmlWebPackPlugin = require("html-webpack-plugin");
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const path = require("path");

module.exports = {
  entry: ["./src/js/index.js", "./src/_scss/main.scss"],
  output: {
    filename: "js/index.js",
    path: path.join(__dirname, "./build/")
  },
  module: {
    rules: [
      {
        test: /\.html$/,
        use: [{ loader: "html-loader", options: { minimize: true } }]
      },
      {
        test: /\.(otf|eot|ttf|woff|svg|png|jpe?g)/i,
        use: [
          {
            loader: "url-loader",
            options: {
              name: "./img/[name].[ext]",
              limit: 10000
            }
          },
          {
            loader: "img-loader"
          }
        ]
      },
      {
        test: /\.scss$/,
        use: ExtractTextPlugin.extract({
          use: [
            {
              loader: "css-loader",
              options: { minimize: true }
            },
            { loader: "postcss-loader" },
            { loader: "sass-loader" }
          ]
        })
      }
    ]
  },
  plugins: [
    new HtmlWebPackPlugin({
      template: "./src/index.html",
      filename: "./index.html"
    }),
    new ExtractTextPlugin({
      filename: "css/main.css"
    })
  ]
};

Ancora qualche sforzo prima di vedere il risultato. Crea il file ./src/_scss/main.scss ed aggiungi all’interno:

$color-orange: #f98a09;

.main {
  background: $color-orange;
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
}

Poi sempre all’interno di ./src/_scss/ aggiungiamo un file di configurazione per autoprefixer, il file deve essere salvato come postcss.config.js:

module.exports = {
  plugins: [require("autoprefixer")]
};

infine, apri package.json e specifica la browser list:

"browserslist": ["last 2 versions"]

A questo punto lanciando il build con:

npm run build

succedono tre cose:

  1. il file SASS viene trasformato in CSS
  2. i vendor prefixes vengono aggiunti al file CSS in uscita
  3. il file CSS viene minimizzato

Non è necessario includere manualmente il CSS all’interno di un tag <link></link>. Webpack se ne occuperà al nostro posto.

Aprendo il file build/index.html con il browser vedrai uno sfondo arancio.

Aprendo poi la console con F12 vedrai invece i prefissi per Flexbox, aggiunti da postcss-loader/autoprefixer.

Passare da Gulp a Webpack: compilare da SASS a CSS, minimizzare gli stylesheets, aggiungere i vendor prefixes
Webpack è in grado di trasformare un file SASS in CSS, oltre ad aggiungere i vendor prefixes con postcss/autoprefixer

Nell’head della pagina invece, vedrai il file CSS automaticamente iniettato da Webpack.

OBIETTIVI 3 E 4 RAGGIUNTI: Webpack può compilare i file SASS in CSS, aggiungere i vendor prefixes e minimizzare gli stylesheet. Passare da Gulp a Webpack porta sicuramente a qualche riga di configurazione in più, ma che risulta molto più leggibile rispetto al Javascript richiesto per impostare 3 tasks su Gulp

Passare da Gulp a Webpack: transpiling del codice Javascript con Babel

Passare da Gulp a Webpack: transpiling del codice Javascript

Quando ho letto “transpiling” per la prima volta ho pensato che io e Javascript non saremmo mai andati d’accordo. Nel frattempo ho cambiato idea ed ho imparato ad usare questo linguaggio solo dove serve.

Transpiling, che non saprei come tradurre in italiano è quel processo con il quale possiamo trasformare codice Javascript moderno (ES6 e successivi) in codice che può essere interpretato da (quasi) tutti i browser.

Prendiamo le arrow function ad esempio. I browser più vecchi non comprendono un’istruzione di questo tipo:

(() => {
  const IAmES6 = `I must be transpiled by Babel`;
  console.log(IAmES6);
})();

Per fare in modo che ES6 venga trasformato in qualcosa di più comprensibile per tutti i browser, il codice deve passare all’interno di una sorta di compilatore, Babel.

Ovviamente non utilizzeremo Babel a mano. Come per tutte le trasformazioni generate da Webpack è necessario installare i loader e aggiungere la relativa configurazione.

Per Babel, abbiamo bisogno di babel-core, babel-loader, babel-preset-env. Aggiungili al progetto con:

npm i babel-core babel-loader babel-preset-env --save-dev

Configura il loader di Babel aggiungendolo alla configurazione di Webpack (per brevità ho omesso il resto del file):

//..
      {
        test: /\.js$/,
        use: {
          loader: "babel-loader",
          options: {
            presets: ["env"]
          }
        }
      }
//..

Ora per testare che la trasformazione Babel funzioni, apri il file ./src/js/index.js ed aggiungi un import:

console.log(`I've been required by Webpack`);
import './es6code'; // nuova riga

passo successivo, crea il file ./src/js/es6code.js:

(() => {
  const IAmES6 = `I've been transpiled by Babel`;
  console.log(IAmES6);
})();

Lancia il build con:

npm run build

e vedrai che il file build/js/index.js conterrà oltre al resto anche il codice opportunamente trasformato:

//
(function () {
  var IAmES6 = "I've been transpiled by Babel";
  console.log(IAmES6);
})();
//

OBIETTIVO 5 RAGGIUNTO: trasformare il codice Javascript con Babel e Webpack.

Passare da Gulp a Webpack: compilare automaticamente quando viene rilevata una modifica

Una delle cose che viene naturale avere quando si lavora con il frontend è la possibilità di ri-compilare gli assets ogni volta che viene modificato un file.

Con Webpack non c’è bisogno di scrivere codice ad hoc o quant’altro. Basta semplicemente aggiungere un nuovo comando all’interno di package.json:

"watch": "webpack --watch"

salva, chiudi il file, e goditi la modalità watching:

npm run watch

OBIETTIVO 6 RAGGIUNTO: compilare tutti gli assets automaticamente quando viene rilevata una modifica. Con Webpack basta solo aggiungere un flag.

E questo conclude il passaggio da Gulp a Webpack. Almeno per il momento. Le possibilità di Webpack sono quasi infinite, e la documentazione è molto vasta.

Mentre quella che abbiamo visto fin ora è una scaletta di base per passare da Gulp a Webpack, le modalità di configurazione e le combinazioni possibili non si esauriscono in un solo articolo. Ti invito ad approfondire la documentazione ed a commentare qui sotto se hai domande o altre idee.

Devi passare da Gulp a Webpack tutti i tuoi progetti?

Dipende. Sei a tuo agio più con lo stile di configurazione di Gulp? Rimani con Gulp. Preferisci una configurazione dichiarativa piuttosto che codice Javascript? Passa a Webpack.

Personalmente ritengo che scrivere una configurazione Webpack come questa sia molto meglio che scrivere codice Javascript per Gulp come questo.

Ma come per tutte le cose si tratta anche di gusti.

La configurazione di Webpack può essere letta e intesa da qualsiasi sviluppatore esperto, oltre che essere di facile lettura per tutti i membri di un team.

Ritengo inoltre che ignorare Webpack nel 2018 sarà impossibile oltre che dannoso: React e VueJS utilizzano Webpack di default. Laravel ha abbandonato Gulp per passare a Webpack. E chissà quanti altri progetti stanno facendo lo stesso.

E nel frattempo si fanno avanti anche delle alternative: Brunch è un altro tool da tenere d’occhio.

Grazie e alla prossima!

Valentino Gagliardi

Valentino Gagliardi

Web Developer & IT Consultant, with over 10 year of experience, I'm here to help you developing your next idea.
Valentino Gagliardi

One Reply to “Come passare da Gulp a Webpack: una guida per iniziare”

Leave a Reply

Your email address will not be published. Required fields are marked *