Commentaires
Tu arrives à la fin du cours, félicitations ;). Voici ici quelques "trucs et astuces" pour améliorer ton expérience avec Webpack.

Le mode watch


Si l'on n'utilise pas webpack dev server mais qu'on veut de Webpack qu'il recompile à chaque changement, on peut le faire avec le mode Watch.
Pour cela, il nous suffit d'utiliser :

{"language":"shell","content":"npx webpack --watch","filename":""}

On peut également utiliser npm run build simplement mais en précisant dans notre webpack.config.js "watch" à true, comme suit :

{"language":"text/javascript","content":"module.exports = {\n mode: 'development',\n watch:true,\n entry: {\n app:'./src/app.js', \n },\n\t...\n}","filename":"webpack.config.js"}

Utiliser plusieurs scripts


Comme on a vu au début, il est plus simple de centraliser les scripts dans le package.json. Pas besoin de se rappeler de npx je ne sais quoi, simplement de npm run "nom du script".
Voici plusieurs scripts qui peuvent être utiles :

{"language":"application/json","content":"\"scripts\": {\n \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\",\n \"build\": \"webpack --config webpack.config.js\",\n \"watch\": \"webpack --config webpack.config.js --watch\",\n \"serve\": \"webpack serve --open\"\n },","filename":"package.json"}

Arthur, l'apprenti développeurJe peux en rajouter autant que je veux ?
Autant que tu veux !

Utiliser plusieurs fichiers de configuration


Une bonne pratique consiste à utiliser plusieurs fichiers de configuration. On peut ainsi appliquer différents paramètres pour la compilation selon si l'on est en développement ou en production. En effet, en développement on va vouloir utiliser ESLint, mais quand on compilera en production, on n'a plus besoin de ce plugin. On utilisera aussi webpack-dev-server mais jamais en production. On peut aussi préciser comment Webpack gèrera nos fichiers, et en mode développement il peut être bon lui indiquer de nous laisser les fichiers générés "lisibles". Tu l'auras peut être remarqué, durant tout le cours en mode développement, les fichiers générés étaient difficilement lisibles car ils utilisaient "eval". On va changer ça.
Voici par exemple deux bons fichiers de configuration. Le premier en mode développement, le second pour la production.

{"language":"text/javascript","content":"const path = require('path')\nconst webpack = require('webpack')\nconst HtmlWebpackPlugin = require('html-webpack-plugin');\nconst { CleanWebpackPlugin } = require('clean-webpack-plugin');\nconst ESLintPlugin = require('eslint-webpack-plugin');\n\nmodule.exports = {\n mode: 'production',\n entry: {\n app:'./src/app.js', \n },\n devtool: 'inline-source-map',\n output: {\n filename:'[name].bundle.js',\n path: path.resolve(__dirname, 'public')\n },\n module: {\n rules: [\n {\n test: /\\.js$/,\n exclude: /node_modules/,\n use: {\n loader: \"babel-loader\",\n options: {\n presets: ['@babel/preset-env']\n }\n }\n }\n ]\n },\n plugins: [\n new ESLintPlugin(),\n new CleanWebpackPlugin(),\n new HtmlWebpackPlugin({\n name:\"Gestion de la sortie\"\n })\n ],\n devServer: {\n contentBase: path.join(__dirname, './public'),\n hot:true,\n }\n}","filename":"webpack.dev.js"}

{"language":"text/javascript","content":"const path = require('path')\nconst webpack = require('webpack')\nconst HtmlWebpackPlugin = require('html-webpack-plugin');\nconst { CleanWebpackPlugin } = require('clean-webpack-plugin');\nconst ESLintPlugin = require('eslint-webpack-plugin');\n\nmodule.exports = {\n mode: 'production',\n entry: {\n app:'./src/app.js', \n },\n output: {\n filename:'[name].[contenthash].js',\n path: path.resolve(__dirname, 'public')\n },\n module: {\n rules: [\n {\n test: /\\.js$/,\n exclude: /node_modules/,\n use: {\n loader: \"babel-loader\",\n options: {\n presets: ['@babel/preset-env']\n }\n }\n }\n ]\n },\n plugins: [\n new CleanWebpackPlugin(),\n new HtmlWebpackPlugin({\n name:\"Gestion des sorties\"\n })\n ]\n}","filename":"webpack.prod.js"}


Arthur, l'apprenti développeurOula, j'ai plein de questions ! Déjà, pourquoi dans le webpack.production.js tu as rajouté [hashcontent] après [name] dans le nom de sortie ?
Excellente question. Pensons en terme de navigateur et de cache navigateur dans un premier temps. Tu n'es peut être pas sans savoir que les navigateurs mettent en cache les assets (images, scripts, feuilles de style). Ainsi, quand on visite un site une deuxième fois, il se charge beaucoup plus rapidement. Le problème pour un développeur est donc quand il fait des mises à jour sur le site. Le contenu des assets aura changé, mais c'est le cache qui sera utilisé ! Une solution pour palier à ce problème est de changer le nom de l'asset. Le navigateur ira alors le chercher lui plutôt que celui qu'il a en cache.
Du coup, ce qu'il faut, c'est changer le nom du fichier dès qu'une modification est effectuée. Et pour ça, on peut utiliser un algorithme de hashage.

Arthur, l'apprenti développeurC'est quoi ça encore ?
Les algorithmes de hashage font partie des algorithmes de chiffrement. Le hashage est couramment utilisé pour vérifier l'intégrité des données. Il est aussi utilisé pour stocker des mots de passe dans une base de données. Lorsqu'on utilise une fonction de hashage sur une entité (une chaine de caractère, un document, un fichier entier...), on obtient une chaine de caractères (le hash) de taille fixe qui dépend du contenu de l'entité : dès qu'un seul bit change, dès qu'un seul caractère change, le hash est totalement différent.

Arthur, l'apprenti développeurAh donc en fait dès que notre script est modifié, le hash sera différent ! Donc en intégrant le hash au nom du fichier, on est sûr que dès qu'une modification a eu lieu, le nom change et donc le navigateur utilisera ce fichier plutôt que celui en cache !
Tu as tout compris ;)

Arthur, l'apprenti développeurMais comment on fait dans notre index.html pour appeler notre script du coup ? Car on va pas changer le src="" du script dès qu'on lance le mode production quand même ? Surtout si mon site commence à utiliser plusieurs scripts !
En effet. Heureusement, dans la partie précédente on a vu l'utilisation de HtmlWebpackPlugin. Ce que je ne t'ai pas dit, c'est comment agit ce plugin. En réalité, il modifie pour nous le index.html et en régénère un à notre place à chaque fois. Donc avec webpack-dev-server, c'est super si on veut que notre résultat dans le navigateur change. Mais c'est aussi super quand on utilise des hash. Car le plugin va lui même insérer les scripts avec le bon nom ;)

Arthur, l'apprenti développeurC'est génial ! Bon j'ai encore 2 questions. C'est quoi "devtool: 'inline-source-map'" ?
Cette ligne permet de répondre à la la phrase que j'ai dite juste avant : "On peut aussi préciser comment Webpack gèrera nos fichiers, et en mode développement il peut être bon lui indiquer de nous laisser les fichiers générés "lisibles". Tu l'auras peut être remarqué, durant tout le cours en mode développement, les fichiers générés étaient difficilement lisibles car ils utilisaient "eval". On va changer ça.". Avec inline-source-map, le code sera plus lisible en développement. Plus pratique pour le déboggage !

Arthur, l'apprenti développeurOk, je comprends. Et encore une autre question, à quoi sert le plugin CleanWebpackPlugin ?
Ce plugin sert à nettoyer notre dossier de sortie. Dans notre cas, "public". En effet, si on rajoute des points d'entrée, des librairies etc, qu'on change de nom à nos sorties, des fichiers .js vont être créés puis plus jamais utilisés. C'est encore plus vrai si on utilise un nom de sortie composé du hash du fichier . Car n'oublions pas que à chaque changement le hash va être modifié, donc un nouveau fichier sera créé avec le nouveau nom. Mais l'ancien ne sera pas supprimé ! CleanWebpackPlugin va le supprimer pour nous. N'oublie pas de l'installer si tu désires l'utiliser :

{"language":"shell","content":"npm install --save-dev clean-webpack-plugin","filename":""}

Arthur, l'apprenti développeurGénial !
Bon, du coup il ne reste plus qu'à créer deux nouveaux scripts dans notre package.json.

{"language":"application/json","content":"\"scripts\": {\n \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\",\n \"build\": \"webpack --config webpack.dev.js\",\n \"watch\": \"webpack --config webpack.dev.js --watch\",\n \"serve\": \"webpack serve --config webpack.dev.js --open\",\n \"prod\": \"webpack --config webpack.prod.js\"\n },","filename":"package.json"}

Éviter la duplication dans les fichiers de configuration


Utiliser plusieurs fichiers de configuration c'est vraiment top. Par contre, c'est dommage que tant de lignes soient dupliquées entre les deux fichiers... On peut évidemment palier à ce problème. Avec webpack-merge.

{"language":"shell","content":"npm install --save-dev webpack-merge","filename":""}

Ensuite on crée un fichier de configuration commun qui reprend ce que nos deux fichiers partageaient.

{"language":"text/javascript","content":"const path = require('path')\nconst webpack = require('webpack')\nconst HtmlWebpackPlugin = require('html-webpack-plugin');\nconst { CleanWebpackPlugin } = require('clean-webpack-plugin');\nconst ESLintPlugin = require('eslint-webpack-plugin');\n\nmodule.exports = {\n entry: {\n app:'./src/app.js', \n },\n module: {\n rules: [\n {\n test: /\\.js$/,\n exclude: /node_modules/,\n use: {\n loader: \"babel-loader\",\n options: {\n presets: ['@babel/preset-env']\n }\n }\n }\n ]\n },\n plugins: [\n new CleanWebpackPlugin(),\n new HtmlWebpackPlugin({\n name:\"Gestion des sorties\"\n })\n ]\n}","filename":"webpack.common.js"}


Et on réécrit nos deux fichiers :
{"language":"text/javascript","content":"const path = require('path')\nconst { merge } = require('webpack-merge')\nconst common = require('./webpack.common.js');\n\nmodule.exports = merge(common, {\n mode: 'production',\n output: {\n filename:'[name].[contenthash].js',\n path: path.resolve(__dirname, 'public')\n }\n})","filename":"webpack.prod.js"}

{"language":"text/javascript","content":"const path = require('path')\nconst ESLintPlugin = require('eslint-webpack-plugin');\nconst { merge } = require('webpack-merge')\nconst common = require('./webpack.common.js');\n\nmodule.exports = merge(common, {\n mode: 'development',\n devtool: 'inline-source-map',\n output: {\n filename:'[name].bundle.js',\n path: path.resolve(__dirname, 'public')\n },\n plugins: [\n new ESLintPlugin(),\n ],\n devServer: {\n contentBase: path.join(__dirname, './public'),\n hot:true,\n }\n})","filename":"webpack.dev.js"}

Arthur, l'apprenti développeurC'est en effet beaucoup plus propre.
Tu l'as dit !

Minifier le CSS


Un dernier point que j'aimerais te montrer est la minification du CSS. En production, Webpack minifie les fichiers JS. Mais pas les fichiers CSS ! C'est pourtant hyper important. Nous avons encore besoin de deux plugins :

{"language":"shell","content":"npm install --save-dev mini-css-extract-plugin","filename":""}

{"language":"shell","content":"npm install css-minimizer-webpack-plugin --save-dev","filename":""}

Et du coup notre fichier de production :

{"language":"text/javascript","content":"const { merge } = require('webpack-merge')\nconst common = require('./webpack.common.js');\nconst path = require('path')\nconst CssMinimizerPlugin = require('css-minimizer-webpack-plugin');\nconst MiniCssExtractPlugin = require('mini-css-extract-plugin');\n\nmodule.exports = {\n mode: 'production',\n output: {\n filename:'[name].[contenthash].js',\n path: path.resolve(__dirname, 'public')\n },\n module: {\n rules: [\n {\n test: /\\.css$/,\n use: [MiniCssExtractPlugin.loader, 'css-loader'],\n }\n ]\n },\n plugins: [\n new MiniCssExtractPlugin({\n filename: '[name].css',\n chunkFilename: '[id].css',\n }),\n ],\n optimization: {\n minimizer: [\n new CssMinimizerPlugin(),\n ],\n },\n}","filename":"webpack.prod.js"}

Et voilà, on a fait le tour de ce dont je voulais te montrer pour Webpack. Tu as encore plein de choses à paramétrer si tu le désires, et le mieux pour cela sera directement de voir la documentation associée.

Arthur, l'apprenti développeurMerci et à bientôt ! J'ai terminé cette partie
Demander de l'assistance