Proiect Webpack/Babel pentru aplicatii JavaScript ES6

Tutorial - Crearea unui proiect de la zero pentru ES6 folosind Webpack si Babel

Daniel Turcu

8 minute read

In acest articol vom crea un proiect Webpack, capabil sa transforme automat codulul ES6+ in cod ES5 (compatibil cu majoritatea browserelor).

Noile versiuni JavaScript vin cu imbunatatiri importante si am vrea sa le utilizam la dezvoltare. Chiar daca aceste noi caracteristici (let, const, arrow functions, clase etc.) nu sunt disponibile pe toate browserele, putem folosi un transpiler precum BabelJS pentru a transforma codul ES6+ in cod compatibil cu browserele mai vechi.

Este ceea ce vom face in acest tutorial.


Initializarea unui proiect nou:

Intr-un folder nou initializam un nou proiect Node:

> npm init

Raspundem la intrebarile primite:

package name: (00_02z_es6) d3js-es6-starter
version: (1.0.0)
...

npm a generat un nou fisier pentru noi: package.json

Acum putem sa ne adaugam dependentele.


Instalare Webpack

Vom instala webpack si webpack-cli. Webpack este un bundler care ne ajuta sa impachetam codul pentru a fi gata de livrare catre serverul de productie. Odata cu aceasta impachetare a tuturor fisierelor necesare in productie, putem aplica diverse transformari ale codului. In felul asta vom transforma si codul ES6 in cod ES5.

> npm i webpack webpack-cli --save-dev

+ webpack-cli@3.2.1
+ webpack@4.28.4
added 394 packages in 83.275s
  • organizam dependentele proiectului in doua mari categorii, librarii care se vor duce in productie (fara argumentul --save-dev) si librarii utile doar la dezvoltare (folosim argumentul --save-dev).

Crearea comenzilor build

Vom crea comenzi care impacheteaza fisierele intr-un director (dist). Acest director va contine fisiere statice (html, css, js, imagini, fonturi…) ce pot fi trimise catre orice server de web pentru a fi afisate in browser.

Vor fi doua comenzi, una pentru dezvoltare (fisierele nu vor fi minificate) si alta va si minifica fisierele (elimina spatiile, redenumeste variabilele cu altele mai scurte, etc.)

In fisierul package.json:

// package.json

  "scripts": {
    "dev": "webpack --mode development ./src/js/index.js --output ./build/main.js",
    "build": "webpack --mode production ./src/js/index.js --output ./build/main.js"
  },

  • rulam cele doua comenzi din terminal astfel:

> npm run dev si > npm run build

Dar deocamdata nu avem ce impacheta. Inainte sa scriem o mica aplicatie, vom mai instala niste pachete utile.


ES6 / Babel

Webpack nu stie sa interpreteze codul ES6 de unul singur. Are nevoie de ajutor. Si acel ajutor vine de la BabelJS.

Folosind Babel, vom scrie cod ES6+, folosind ultimele specificatii, dar codul final va fi transformat in ES5, compatibil cu majoritatea browserelor.

Webpack foloseste loadere si plugin-uri pentru a prelua codul din fisiere si a aplica diverse transformari asupra lui inainte de a scrie in destinatia finala.

Vom instala si configura componentele Babel pentru Webpack:

  • babel core
  • babel loader
  • babel preset env pentru compilarea codului Javascript ES6 in ES5

> npm i @babel/core babel-loader @babel/preset-env --save-dev

+ @babel/core@7.2.2
+ babel-loader@8.0.5
+ @babel/preset-env@7.2.3

added 113 packages in 14.063s

Configurarea Babel

Cream un nou fisier in radacina proiectului: .babelrc

// .babelrc

    {
        "presets": [
            "@babel/preset-env"
        ]
    }

  • am configurat proprietatea presets cea care ne spune ce versiune JavaScript avem de gand sa scriem in fisierele .js. @babel/preset-env inseamna ca vrem sa scriem in ultima versiune JavaScript (ES6+)

Configuram acum loaderele Babel. Cream un fisier nou de configurare Webpack:

// webpack.config.js

    module.exports = {
      module: {
        rules: [
          {
            test: /\.js$/,
            exclude: /node_modules/,
            use: {
              loader: "babel-loader"
            }
          }
        ]
      }
    };


Testam ES6

Haideti sa facem un mic test sa vedem daca ceea ce scriem noi in ES6 se va compila in ES5:

Pentru asta vom crea un fisier in calea src/js/index.js:

// src/index.js

class MyClass {
    constructor() { }

    print() {
        console.log('Test ES6')
    }
}

let c = new MyClass();
c.print();


  • am scris o clasa in ES6, ma astept ca dupa compilare sa ajunga la destinatie ca o functie (nu avem clase in ES5).

Rulam comanda > npm run dev

...
  Asset      Size  Chunks             Chunk Names
main.js  4.77 KiB    main  [emitted]  main
Entrypoint main = main.js
[./src/js/index.js] 974 bytes {main} [built]

Verificam continutul fisierului build/main.js si cautam numele clasei MyClass. In loc sa gasim class MyClass vom gasi function MyClass:

“Babel compileaza codul ES6 in ES5”

Acum avem tot ce ne trebuie ca sa compilam cod ES6, insa vom mai instala cateva componente care ne usureaza dezvoltarea, inclusiv un server web de dezvoltare care sa reincarce automat aplicatia web la fiecare modificare in fiserele sursa.


Instalare plugin pentru procesare HTML

Vrem ca Webpack sa preia automat un template index.html si sa-l duca in build. Pe langa asta vrem sa incarce si bundle-ul final in pagina (main.js).

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

+ html-loader@0.5.5
+ html-webpack-plugin@3.2.0

added 46 packages in 17.08s

Acum cream un fisier src/index.html:

<!-- src/index.html -->

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>ES6, Webpack and Babel Starter</title>
  </head>
  <body>
    <h1> ES6, Webpack and Babel - Starter Project </h1>
    <div id="app"></div>
  </body>
</html>

Configurare html-webpack-plugin

Adaugam urmatorul cod in webpack.config.js:

// webpack.config.js

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

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

Acum putem verifica functionarea plugin-ului. Ne asteptam sa apara si fisierul build/index.html. Iar daca verificam continutul, observam ca incarca fisierul main.js.

> npm run dev

Built at: 2019-01-17 15:14:12
       Asset       Size  Chunks             Chunk Names
./index.html  208 bytes          [emitted]
     main.js   4.77 KiB    main  [emitted]  main
Entrypoint main = main.js
[./src/js/index.js] 974 bytes {main} [built]

In build/index.html a aparut si urmatorul cod: <script type="text/javascript" src="main.js"></script>


Prelucrarea CSS (preluarea si transferul in fisier CSS separat)

Implicit, Webpack nu stie sa prelucreze CSS intr-un fisier independent de aplicatia principala (main.js).

Vom folosi plugin-ul mini-css-extract-plugin pentru a extrage CSS in bundle separat, dedicat stilurilor CSS.

vom folosi loaderul css-loader pentru a instrui Webpack cum sa prelucreze fisierele .css, adica ce sa faca atunci cand intalneste un fisier .css. Aceste fisiere .css le vom trata ca pe niste module si le importam din fisiere .js. Pentru fiecare fisier .js care importa .css, Webpack va incarca acel fisier .css si il va trimite catre un bundle main.css.

Instalam mini-css-extract-plugin si css-loader

> npm i mini-css-extract-plugin css-loader --save-dev

+ mini-css-extract-plugin@0.5.0
+ css-loader@2.1.0

added 20 packages in 17.312s

Apoi, configuram pluginul:

// webpack.config.js

...
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
  module: {
    rules: [
      {
        ...
        loader: "babel-loader"
        ...
      },
      {
        ...
        loader: "html-loader",
        ...
      },
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, "css-loader"]
      }
    ]
  },
  plugins: [
    new HtmlWebPackPlugin({
      ...
    }),
    new MiniCssExtractPlugin({
      filename: "[name].css",
      chunkFilename: "[id].css"
    })
  ]
};

Urmeaza sa testam functionalitatea plugin-ului si pentru asta cream un nou fisier .css:

/* src/js/index.css */

h1 {
    color: #0044af;
    font-size: 0.9rem;
}


Incarcam css-ul in fisierul index.js, ca modul:

// src/index.js

import styles from "./index.css";
...

Acum css-ul este prelucrat de loader si trimis in directorul build.

Rulam comanda > npm run dev

Built at: 2019-01-17 18:43:03
       Asset       Size  Chunks             Chunk Names
./index.html  292 bytes          [emitted]
    main.css   59 bytes    main  [emitted]  main
     main.js   5.45 KiB    main  [emitted]  main
Entrypoint main = main.css main.js

Directorul build contine si un bundle special pentru css-uri, fisierul main.css iar index.html are cod de incarcare a lui in pagina:

<!-- build/index.html -->

    ...
    <link href="main.css" rel="stylesheet">
</head>

Putem sa testam aplicatia finala cu un server web sau deschizand direct index.html, insa vrem sa instalam un server de dezvoltare care sa actualizeze automat aplicatia si sa o reincarce in browser pentru noi.


Server Web pentru dezvoltare

Mai avem putin si terminam proiectul nostru ES6. Vom instala un server pentru dezvoltare (modulul webpack-dev-server):

> npm i webpack-dev-server --save-dev

+ webpack-dev-server@3.1.14

added 139 packages in 15.9s

Definim comanda care va porni serverul de dezvoltare:

// webpack.config.js

"scripts": {
  "start": "webpack-dev-server --mode development ./src/js/index.js --open",
  "dev": "..."
}

– argumentul --open va deschide si browserul implicit

Acum pornim serverul cu comanda > npm run start (comanda start poate fi pornita si cu > npm start).

Cu serverul pornit, incercati sa modifiati codul sursa (src/index.html, src/js/index.css sau src/js/index.js) si observam cum se actualizeaza aplicatia in browser.


Extra - minify CSS

Opriti serverul de dezvoltare (CTRL ^C) si incercati sa rulati alternativ comenzile:

> npm run dev > npm run build

Prima comanda nu aplica minificare a doua aplica. Observam, in cazul comenzii build, ca main.js este minificat dar nu si main.css.

Pentru asta avem nevoie sa definim un minifier extern de CSS. Webpack ne sa posibilitatea sa facem asta, dar va trebui sa furnizam un minifier si pentru .js pentru ca nu va mai functiona cel intern.

Deci, vom instala plugin-uri externe pentru minificare. Apoi vom suprascrie setarea optimization.minimizer.

  • odata suprascrisa aceasta setare, nu mai functioneaza minificarea pentru fisierele .js
  • asa ca trebuie sa instalam si un plugin pentru minificarea fisierelor .js

Instalam plugin-urile:

> npm i optimize-css-assets-webpack-plugin uglifyjs-webpack-plugin --save-dev

+ optimize-css-assets-webpack-plugin@5.0.1
+ uglifyjs-webpack-plugin@2.1.1

added 103 packages in 14.501s

Configuram plugin-urile de minificare:

// webpack.config.js

...
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");

module.exports = {
  optimization: {
    minimizer: [
      new UglifyJsPlugin({
        cache: true,
        parallel: true,
        sourceMap: true
      }),
      new OptimizeCSSAssetsPlugin({})
    ]
  },
  ...
}

Rulam alternativ comenzile build si dev:

> npm run build > npm run dev

  • observam ca build minifica codul iar dev creeaza doar bundle-ul dar nu aplica minificarea
  • si face asta atat pentru CSS cat si pentru JS

Codul CSS minificat:

/* build/main.css */
h1{color:#0044af;font-family:sans-serif}

Concluzie

In acest tutorial am pus cap la cap, de la zero, tehnologii web necesare pentru dezvoltarea aplicatiilor JavaScript ES6+. Am creat un proiect starter care poate fi folosit ca punct de plecare pentru alte proiecte care necesita ES6 transpiling - conversia codului ES6 in cod ES5, compatibil cu mai toate browserele.

Pentru asta am folosit Webpack, Babel si loadere si plugin-uri pentru Webpack.

Proiectul stie sa compileze automat sursele si sa retrimita in browser actualizarile fara sa fie nevoie de Browser Refresh (F5). Developerii se pot concentra pe cod.

comments powered by Disqus