Présentation

Webpack est un outil très pratique pour développer facilement et rapidement des applications ReactJs. Il permet de transformer des syntaxes modernes et agréables à coder en un code JavaScript classique interprétable par les navigateurs web.

Lorsqu’on commence à utiliser ReactJs avec webpack, on se retrouve rapidement avec un code généré de plus de 1MB, ce qui est beaucoup trop et qui peut inquiéter les personnes qui comme moi, veulent tester la viabilité de cette technologie.

Cependant, webpack permet d’aller beaucoup plus loin dans la configuration pour optimiser les fichiers générés, en limitant leur taille pour une utilisation en production ou en embarquant un module de live reload pour un développement plus efficace.

Nous allons donc étudier deux configurations pour webpack, une pour le développement qui propose proposer le live reload et facilite le debugging et une autre pour la production, en optimisant le code généré.

Contexte

Nous utiliserons comme support le projet présenté dans l’article ReactJs : Comment configurer un projet webpack. Passons rapidement sur ses caractéristiques :

  • app/main.js est le point d’entrée de l’application
  • index.js est le bundle qui sera généré par webpack
  • babel est le loader qui sera utilisé pour gérer les syntaxes ES6 et JSX

Le fichier webpack.config.js minimal nécessaire pour ce projet est le suivant :

module.exports = {
    entry: "./app/main.js",
    output: {
        path: './',
        filename: 'index.js',
    },
    module: {
        loaders: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: 'babel',
                query: {
                    presets: ['es2015','react']
                }
            },
        ]
    }
};

Cette configuration contient tous les éléments nécessaires pour créer le bundle index.js en exécutant la commande :

$ webpack
Hash: 7abec28c24bfc081d59e
Version: webpack 1.14.0
Time: 3276ms
   Asset    Size  Chunks             Chunk Names
index.js  740 kB       0  [emitted]  main
    + 173 hidden modules

Configuration de production

Nous attendons d’une configuration de production qu’elle fournisse le bundle le plus léger possible pour ne pas impacter les performances de la page web qui hébergera l’application. Nous allons donc enrichir la configuration précédente pour arriver à ce résultat.

Structure du webpack.prod.config.js

Nous allons créer un nouveau fichier webpack.prod.config.js qui contiendra la configuration de production.

La première solution pour réduire la taille du bundle est de choisir le bon devtool. Il s’agit du paramètre qui spécifie comment Webpack gère la source map : un mapping entre le code source et le code généré (très pratique pour le débugging).

Si quelqu’un a encore un doute, débugger en prod… c’est mal, m’voyez ! Et c’est surtout trop tard ! Nous pouvons donc nous contenter d’une petite source map, pour ce faire, nous utilisons l’option production-ready de l’outil de développement cheap-module-source-map qui minimise au maximum le fichier généré.

Dans webpack.prod.config.js :

module.exports = {
    ...
    devtool: "cheap-module-source-map",
    ...
}

Avec cette option, au lieu de créer un gros fichier, webpack va en créer 2 :

  • un premier contenant uniquement le code de l’application
  • un second contenant la source-map.

À elle seule, cette option permet de réduire le poid du bundle de plus de 70%

Le bundle contient encore des helpers et des tests, ce qui n’a pas réellement d’intérêt en production. Nous pouvons utiliser un plugin de webpack pour exclure ces éléments du build. Cela se fait simplement en ajoutant un nouveau champ dans le fichier de configuration :

var webpack = require('webpack');
module.exports = {
    ...
    plugins: [
        new webpack.DefinePlugin({
            'process.env': {
                'NODE_ENV': JSON.stringify('production')
            }
        }),
    ]
    ...
}

Avec ce plugin, nous pouvons encore réduire de 30% le poid du bundle.

Nous obtenons finalement le fichier webpack.prod.config.js suivant :

var webpack = require('webpack');
module.exports = {
    devtool: "cheap-module-source-map", 
    entry: "./app/main.js",
    output: {
        path: './',
        filename: 'index.js',
    },
    module: {
        loaders: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: 'babel',
                query: {
                    presets: ['es2015','react']
                }
            }
        ]
    },
    plugins: [
        new webpack.DefinePlugin({
            'process.env': {
                'NODE_ENV': JSON.stringify('production')
            }
        }),
    ]
};

Lancement de Webpack

Nous lançons webpack en lui spécifiant le fichier de configuration à utiliser, nous lui précisons qu’il s’agit d’un build de production avec l’option -p (pour qu’il minifie automatiquement le code).

$ webpack -p --progress --config webpack.prod.config.js
Hash: 64d12896f8259c2f73fc
Version: webpack 1.14.0
Time: 5506ms
       Asset       Size  Chunks             Chunk Names
    index.js     149 kB       0  [emitted]  main
index.js.map  147 bytes       0  [emitted]  main
    + 164 hidden modules

Avec très peu de modification de la configuration nous observons que le fichier index.js, qui pesait 740kB avec la configuration de base fait maintenant 149kB, soit un gain de 80% !

Configuration de développement

La problématique lors du développement est différente, qu’importe la taille du fichier, nous voulons un développement agréable et un debugging simple. Notre objectif est donc d’avoir un code client lisible se rechargeant automatiquement à chaque modification.

Structure du webpack.config.js

Webpack étant un outil de développement, tout est quasiment géré automatiquement. La seule chose à faire et de spécifier la configuration du serveur qui se chargera du live-reload.

Cette configuration se fait dans le fichier webpack.dev.config.js

module.exports = {
    ...
    devServer: {
        inline: true,
        port: 8080
    },
    ...
};

Nous voulons que le serveur utilise le port 8080 et qu’il fonctionne en mode inline.

Finalement, la configuration de développement est :

module.exports = {
    entry: "./app/main.js",
    output: {
        path: './',
        filename: 'index.js',
    },
    devServer: {
        inline: true,
        port: 8080
    },
    module: {
        loaders: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: 'babel',
                query: {
                    presets: ['es2015','react']
                }
            }
        ]
    }
};

Lancement de Webpack

Pour lancer la transpilation, il suffit de spécifier le ficher de configuration à utiliser.

webpack --config webpack.dev.config.js
Hash: 7abec28c24bfc081d59e
Version: webpack 1.14.0
Time: 6509ms
   Asset    Size  Chunks             Chunk Names
index.js  740 kB       0  [emitted]  main
    + 173 hidden modules

La taille du bundle ne change pas par rapport à notre configuration de base parce que le code n’est pas minifié par défaut. De plus, Webpack ajoute automatiquement un module pour gérer le live reload.

Le reste de la magie sera géré par le serveur que nous allons utiliser : webpack-dev-server.

$ webpack-dev-server --config webpack.dev.config.js

C’est lui qui écoute les modifications faites sur les fichiers du projet. À chaque modification, il relance webpack avec la configuration qui lui a été donnée. Puis, il notifie le client du changement pour que le navigateur se mette à jour automatiquement.

Conclusion

Nous venons de créer rapidement et facilement des configurations dédiées au développement et à la production adaptées aux besoins les plus courants.

Il faut également faire un travail sur les modules importés et la qualité de notre code, mais il est rassurant de voir que webpack nous permet de parcourir une partie du chemin sans trop de difficulté.

Il en faudra certainement plus pour convaincre les détracteurs de ReactJs trouvant que son fonctionnement est un peu trop « magique », mais cela pourra peut être rassurer ceux qui sont tentés de le mettre en place sur de petits projets.