Présentation
Selon la documentation de webpack :
Au coeur de webpack se trouve un "empaqueteur" (bundler) pour les applications javascript modernes. Lorsque webpack traite votre application, il crée en interne un graphique de dépendances qui cartographie chaque module dont votre projet a besoin et génère un ou plusieurs "bundle".
Pour résumé, webpack vous aide à gérer les imports et exports de fichiers de votre projet et il va regrouper tout votre code dans un seul fichier appelé "bundle".
Pour comprendre l'intérêt de webpack, il faut avoir conscience qu'à chaque utilisation d'un fichier dans une page html (css, js, images...) une requête http est effectuée. webpack vous permet donc de minimiser le nombre de requêtes.
Principales fonctionnalités
Possibilité d'utilisation de
- Babel pour transpiler le JS et le rendre ainsi rétrocompatible
- compresseur/minificateur de code en vue de réduire la latence réseau
- compilateur de SASS
- optimisateur d' images
Premier exemple d'utilisation
imaginons que vous ayez 4 fichiers js
src/first.js
export const first = 1
src/second.js
export const second = 2
src/third.js
import { first } from './first' import { second } from './second' export const third = first + second
src/main.js
import { third } from './third' console.log(third)
Nous comprenons qu'il va falloir faire attention à l'ordre d'importation des fichiers pour que main.js fonctionne correctement.
C'est justement le travail de webpack !
Installation de webpack-cli
npm install --save-dev webpack-cli
Création du fichier de config webpack.config.js
A noter, lorsque que vous utilisez create-react-app, la config de webpack se trouve dans \node_modules\react-scripts\config\webpack.config.js
// require est une fonction native de Node.js qui renvoie un objet utilitaire // possède des méthodes comme "resolve" qui permet de générer des chemins // absolues sur tous les systèmes d'exploitation const path = require("path") const config = { mode: "development", // Point d'entrée pour webpack entry: { myApp: [ "./src/main.js", ], }, // Sortie pour webpack output: { // Depuis le répertoire courant vers le répertoire dist qui contiendra les bundles path: path.resolve(__dirname, "dist/"), filename: "bundle.js" }, } module.exports = config
Lancement de webpack
npx webpack build
Vérification
node dist/bundle.js// 3 attendu
Loaders
Par défaut, Webpack ne comprend que le JS et le JSON. Les loaders permettent de charger tous les autres types de fichiers afin que Webpack les ajoute à l’arbre des dépendances
Loader TypeScript
Pour commencer, renommer tous les fichiers js en ts : maintenant, nous codons en typescript !
Installation de typescript et de ts-loader :
npm install --save-dev typescript ts-loader //--save-dev (ou -D) : packages dont on a besoin uniquement en phase de développement
Configuration de typescript (fichier tsconfig.json)
{ "compilerOptions": { "outDir": "./dist/", "noImplicitAny": true, "module": "es6", "target": "es6", "jsx": "react", "allowJs": true, "moduleResolution": "node" } }
Modification de webpack.config.js
const path = require("path"); const config = { mode: "development", // Point d'entrée de Webpack entry: { myApp: ["./src/main.ts"], }, module: { rules: [ { test: /\.tsx?$/, // pour les fichiers se terminant pas ts ou tsx exclude: /node_modules/, // sans prendre en compte les dépendances use: "ts-loader", }, ], }, resolve: { extensions: [".tsx", ".ts", ".js"], }, output: { filename: "bundle.js", path: path.resolve(__dirname, "dist"), }, }; module.exports = config;
Vérification
Changer l'extension de tous les fichiers js en ts.
Relancer le build :
npx webpack build
Exécuter le bundle :
node dist/bundle.js// 3 attendu
sass-loader
Installation
npm install sass-loader css-loader style-loader sass webpack --save-dev
Configuration
Modifier le fichier webpack.config.js
module: { rules: [ { test: /\.tsx?$/, // pour les fichiers se terminant pas ts ou tsx exclude: /node_modules/, // sans prendre en compte les dépendances use: "ts-loader", }, { test: /.scss$/, use: ["style-loader", "css-loader", "sass-loader"], }, ], },
Pour importer vos fichiers scss :
import './css/tasks.scss';
Plugins
Les plugins permettent de réaliser des opérations globales lors de "l'empaquetage" (bundling)
Par exemple HtmlWebpackPlugin simplifie la création de fichier HTML pour servir les bundles webpack.
Installation de HtmlWebpackPlugin
npm install --save-dev html-webpack-plugin
Modification de webpack.config.js :
const HtmlWebpackPlugin = require("html-webpack-plugin"); const path = require("path"); const config = { mode: "development", // Point d'entrée de Webpack entry: { myApp: ["./src/main.ts"], }, module: { rules: [ { test: /\.tsx?$/, // pour les fichiers se terminant pas ts ou tsx exclude: /node_modules/, // sans prendre en compte les dépendances use: "ts-loader", } ], }, resolve: { extensions: [".tsx", ".ts", ".js"], }, output: { filename: "bundle.js", path: path.resolve(__dirname, "dist"), }, //plugins: [new HtmlWebpackPlugin()], plugins: [new HtmlWebpackPlugin({ template: __dirname + '/src/index.html' })],
}; module.exports = config;
Vous verrez qu'au build (npx webpack build), le fichier dist/index.html est créé.
Si vous souhaitez utiliser votre propre fichier html, utilisez le paramétrage suivant :
plugins: [new HtmlWebpackPlugin({ template: __dirname + '/src/index.html' })],
Installation de webpack-dev-server
npm install webpack-dev-server --save-dev
Modification de package.json
{ "devDependencies": { "css-loader": "^6.8.1", "html-webpack-plugin": "^5.5.3", "sass": "^1.66.1", "sass-loader": "^13.3.2", "style-loader": "^3.3.3", "ts-loader": "^9.4.4", "typescript": "^5.2.2", "webpack": "^5.88.2", "webpack-cli": "^5.1.4", "webpack-dev-server": "^4.15.1" }, "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "webpack-dev-server", "build": "NODE_ENV=production webpack" } }
Lancement du serveur
npm start// lance le serveur sur le port 8080: http://localhost:8080/
Ajouter devServer: {open: true}, dans le fichier webpack.config.json pour que le navigateur s'ouvre automatiquement
React
Utiliser Babel
Pour ajouter Typescript, deux choix s'offrent à nous.
- TypeScript Compiler (tsc) transpile TS en ES5 JS. tsc vérifie aussi les types.
- Babel transpile le TS. et tsc vérifie le type.
Babel a plus de flexibilité au niveau de la transpilation et permet le polyfill ( Recréer des fonctions inexistantes en ES5 mais qui existe en ES6 (Exemple : Array.prototype.find). De plus, les responsabilités sont claires :
- Babel transpile
- tsc vérifie les types.
Installer React et React-Dom
npm i -D react react-dom
Installer Babel
Babel est un compilateur qui transpile le code :
- De JSX au Js standard
- De ESNEXT à ES5
npm i -D @babel/core babel-loader
- @babel/core : La bibliothèque principale de Babel.
- babel-loader : permet d'utiliser Babel avec Webpack.
Remplacer ts-loader par babel-loader dans le webpack.config.js
rules: [ { test: /\.tsx?$/, // pour les fichiers se terminant pas ts ou tsx exclude: /node_modules/, // sans prendre en compte les dépendances use: "babel-loader", },
Installer le transpileur
npm i -D @babel/preset-react
Pour faire fonctionner les applications sur de vieux navigateurs, il faut
- Transpiler le code de ES6+ à ES5- : Remplacer le nouveau JS par de l'ancien
- "Polyfill" : Recréer des fonctions inexistantes en ES5 mais qui existe en ES6 (Exemple : Array.prototype.find)
Installer les modules pour le polyfill
npm i -D @babel/preset-env core-js regenerator-runtime
Le module clé est @babel/preset-env et core-js. L'évolution de JavaScript étant très rapide, des centaines de fonctionnalités doivent être transposées et "polyfill". Mais heureusement, @babel/preset-env est un preset de transpilation intelligent qui couvre la plupart des fonctionnalités. Et core-js est un preset polyfill. Lorsque vous utilisez des fonctionnalités que @babel/preset-env et core-js ne supportent pas, il est temps de penser à utiliser des plugins. regenerator-runtime est pour les promesses.
Créer fichier babel.config.json à la racine
{ "presets": [ [ "@babel/preset-env", { // ajoute au début de chaque fichier l'importation de polyfills pour les fonctionnalités utilisées // dans ce fichier et non supportées par les environnements cibles "useBuiltIns": "usage", "corejs": 3 // Numéro de version, la 3 est maintenue } ], "@babel/preset-react" ] }
Installer Typescript
Installer le transpileur Typescript
npm i -D @babel/preset-typescript
Ajouter @babel/preset-typescript dans babel.config.json.
{ "presets": [ "@babel/preset-typescript", ... ] }
Installer les types de React
npm i -D @types/react @types/react-dom
Ajouter dans le tsconfig.json
Babel se charge de la compilation de typescript, "noEmit" permet de s'assurer que ce n'est pas tsc qui gère la transpilation
esModuleInterop permet de gérer les imports typescript au sein de React
"compilerOptions": { "noEmit": true, "esModuleInterop": true, ... }
et enlever "outDir" puisque tsc ne s'occupe plus de la transpilation
"outDir": "./dist/",
Ajouter le script pour le type-checking dans le package.json
"script": { ... "tscheck": "tsc -w" }
Modifier le htmlWebpackPlugin dans le webpack.config.js (ajout du template pour pouvoir ajouter son propre html dans index.html)
plugins: [new HtmlWebpackPlugin({template: "./dist/index.html"})]
Ajouter une div avec l'attribut id="root" dans dist/index.html
Créer un index.tsx dans le dossier src
import React from 'react'; import {createRoot} from 'react-dom/client'; import {App} from './App'; const rootNode = document.getElementById('root'); if (rootNode) { createRoot(rootNode) .render(<App/>); }
Modifier l'entrée dans le webpack.config.js par index.tsx
Créer App.tsx dans le dossier src
import React from 'react'; import Count from "./Count"; export const App = () => ( <> <h1>Hello React</h1> <Count/> </> );
Créer Count.tsx dans le dossier src
import React from 'react'; function Count() { return ( <div> 1 </div> ); } export default Count;
Relancer l'application
npm start
Utiliser SASS
Installation
npm i -D sass style-loader css-loader sass-loader
Modifier le fichier webpack.config.js
module: { rules: [ ... { test: /\.scss?$/, use: ['style-loader', 'css-loader', 'sass-loader'] } ], },
Créer un fichier scss dans src et l'importer dans main.js
Relancer l'application
npm start
Installer bootstrap
npm install react-bootstrap bootstrap
importer les fichiers sources de bootstrap
Ajouter dans index.scss par exemple :
$warning: green; @import "~bootstrap/scss/bootstrap";
Importer votre fichier index.scss dans index.tsx :
import "./sass/custom.scss";
Emuler un serveur à l'aide de json-server.
Installer json-server
npm i -D json-server
Créer un fichier db.json à la racine
Attention, pour que json-server fonctionne, Il faut que chaque objet possède une propriété id
exemple:
{ "posts": [ { "id": 1, "title": "Webpack", "author": "Bob" }, { "id": 2, "title": "Typescript", "author": "Josianne" }, { "id": 3, "title": "json-server", "author": "Simon" } ] }
Créer un script dans package.json pour lancer le serveur
"json-server": "json-server --watch db.json"
Lancer json-server
npm run json-server
Executer une requête
Les routes utilisables :
Requête Http | Route |
---|---|
GET | /posts |
GET | /posts/1 |
POST | /posts |
PUT | /posts/1 |
PATCH | /posts/1 |
DELETE | /posts/1 |
Pas besoin de préciser l'id dans le corps de votre requête POST / PUT / PATCH
POST / PUT / PATCH doivent inclure Content-Type: application/json dans leur header de requête.
Exemple de requête GET :
fetch("http://localhost:3000/posts") .then(response => { console.log(`response status`, response.status); return response.json(); }) .then(data => { console.log(`data : `, data); }) .catch(error => { console.log(`erreur attrapée : `, error); })
Exemple de requête POST :
fetch("http://localhost:3000/posts", { headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }, method: "POST", body: JSON.stringify({ "title": "Simon", "author": "Yvan" }) }) .then(function (res) { console.log(res) }) .catch(function (res) { console.log(res) })
Exercice 1
Temps : entre 3 et 5 heures.
Par groupe de 3, choisir un exemple d'application (ex : boutique en ligne) et gérer à minima un "CRUD".
Utiliser :
- react + ts + jsx + bootstrap + sass .Bootstrap est optionnel, vous pouvez vous en passer ou utiliser un équivalent
- json-server ou des api de votre choix (n'y passez pas plus de 30mn pour les choisir)
- fetch, await, async
- les hooks : à minima useState et useEffect mais d'autres hooks comme userRef, useContext, useReducer, ... peuvent être utiles
- une arborescence claire : src/components, src/services, src/sass, src/interfaces ...
- Partagez votre code sur un repository github
- Préparez une présentation qui explique les points clés
- Entre-aidez vous !!!