L’objectif de cet article est de créer une application React sans create-react-app et de comprendre ainsi les processus qui entrent en jeu dans la création d’une telle application en utilisant JSX, Babel, Webpack et les autres outils, langages, plugins etc…
Babel est un transcompilateur Javascript Open Source et est principalement utilisé pour convertir le code ECMAScript 2015+ (ES6+) en une version compatible avec les moteurs Javascript plus anciens, donc pouvant être lancés dans les navigateurs Web.
Webpack est un bundler (groupeur) permettant d’utiliser un serveur local, utiliser le Live Reload, mais aussi et surtout compiler tous les fichiers javascript en un seul. Pratique notamment pour la performance de l’application.
JSX est une extension du langage javascript permettant de définir des éléments React à la manière du XHTML.
On vérifie que Node et npm sont installés :
1 2 3 4 |
xavier@server:~/www/_murviel-info/react$ node -v v12.18.2 xavier@server:~/www/_murviel-info/react$ npm -v 1.22.5 |
On utilisera yarn
que l’on installe globalement avec sudo
, yarn
accélère grandement les processus npm :
1 2 3 |
xavier@server:~$ sudo npm -g install yarn xavier@server:~$ yarn -v 1.22.10 |
Initialisation
On crée un dossier pour le projet. Je l’appelle mib-colors
, et l’on s’y dirige, on initialise avec yarn
.
1 2 3 |
xavier@server:~$ mkdir mib-colors xavier@server:~$ cd mib-colors xavier@server:~/mib-colors$ yarn init -y |
Cela crée un fichier package.json :
1 2 3 4 5 6 7 |
xavier@sserver:~/mib-colors$ cat package.json { "name": "mib-colors", "version": "1.0.0", "main": "index.js", "license": "MIT" } |
Installation de React
, Webpack
et Babel
On installe plusieurs packages pour fonctionner : Babel
pour le jsx
et webpack
pour gérer la transpilation et l’environnement.
Un compilateur source à source, transpileur ou transcompilateur est un type de compilateur qui prend le code source d’un langage de programmation et le compile dans un autre langage de programmation. Un transpileur opère sur deux langages avec le même niveau d’abstraction, alors qu’un compilateur traditionnel compile un langage de haut niveau vers un langage de bas niveau.
On installe ces modules avec yarn
. L’instruction add
permet d’ajouter des packages. L’option -D
permet de partitionner les dépendances entre le développement et la production. Les dépendances installées avec ce paramètre ne sont pas incluses pour la production. On installe tout pour le développement. Une fois l’application construite, elle fonctionnera dans un navigateur, en l’incluant de façon habituelle pour un fichier javascript.
1 |
xavier@server:~/mib-colors$ yarn add -D @babel/core @babel/preset-env @babel/preset-react webpack webpack-cli webpack-dev-server babel-loader css-loader style-loader html-webpack-plugin |
Ces commandes créent un dossier node_modules
contenant les différents fichiers des modules et des dépendances. Après la commande, on obtient un arbre des ces modules :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
yarn add v1.22.10 info No lockfile found. [1/4] Resolving packages... warning webpack-dev-server > chokidar@2.1.8: Chokidar 2 will break on node v14+. Upgrade to chokidar 3 with 15x less dependencies. warning webpack-dev-server > chokidar > fsevents@1.2.13: fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2. warning webpack-dev-server > chokidar > braces > snapdragon > source-map-resolve > resolve-url@0.2.1: https://github.com/lydell/resolve-url#deprecated warning webpack-dev-server > chokidar > braces > snapdragon > source-map-resolve > urix@0.1.0: Please see https://github.com/lydell/urix#deprecated [2/4] Fetching packages... info fsevents@1.2.13: The platform "linux" is incompatible with this module. info "fsevents@1.2.13" is an optional dependency and failed compatibility check. Excluding it from installation. [3/4] Linking dependencies... [4/4] Building fresh packages... success Saved lockfile. success Saved 436 new dependencies. info Direct dependencies ├─ @babel/core@7.13.14 ├─ @babel/preset-env@7.13.12 ├─ @babel/preset-react@7.13.13 ├─ babel-loader@8.2.2 ├─ css-loader@5.2.0 ├─ html-webpack-plugin@5.3.1 ├─ style-loader@2.0.0 ├─ webpack-cli@4.6.0 ├─ webpack-dev-server@3.11.2 └─ webpack@5.30.0 info All dependencies ├─ @babel/compat-data@7.13.12 ├─ @babel/core@7.13.14 ├─ @babel/helper-builder-binary-assignment-operator-visitor@7.12.13 ├─ @babel/helper-compilation-targets@7.13.13 ├─ @babel/helper-explode-assignable-expression@7.13.0 ├─ @babel/helper-get-function-arity@7.12.13 ├─ @babel/helper-hoist-variables@7.13.0 ├─ @babel/helper-member-expression-to-functions@7.13.12 ├─ @babel/helper-simple-access@7.13.12 ├─ @babel/helper-wrap-function@7.13.0 ... |
Le fichier package.json
devient :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
xavier@server:~/mib-colors$ cat package.json { "name": "mib-colors", "version": "1.0.0", "main": "index.js", "license": "MIT", "devDependencies": { "@babel/core": "^7.13.14", "@babel/preset-env": "^7.13.12", "@babel/preset-react": "^7.13.13", "babel-loader": "^8.2.2", "css-loader": "^5.2.0", "html-webpack-plugin": "^5.3.1", "style-loader": "^2.0.0", "webpack": "^5.30.0", "webpack-cli": "^4.6.0", "webpack-dev-server": "^3.11.2" } } |
Résolution des problèmes d’installation
fsevents
fsevents
est une dépendance facultative. Cette dépendance n’est nécessaire que si l’on utilise LedgerSubprovider
dans un environnement Node et n’est pas pris en charge dans Linux sans dépendances supplémentaires. On peut ignorer cet avertissement.
webpack-dev-server
Il y a un souci de compatibilité entre webpack-dev-server
et chockidar
. webpack-dev-server
est un module permettant d’ouvrir une connexion et de recompiler les sources en cas de changement du code pendant le développement : live reloading. Il n’est utilisé que pour le développement.
On va laisser comme cela et l’on verra les problèmes en lançant le serveur de développement avec la commande webpack serve
.
React
et React-dom
On ajoute les packages React
et React-dom
pour notre application, c’est essentiel pour qu’elle puisse fonctionner avec React.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
xavier@server:~/mib-colors$ yarn add react react-dom yarn add v1.22.10 [1/4] Resolving packages... [2/4] Fetching packages... info fsevents@1.2.13: The platform "linux" is incompatible with this module. info "fsevents@1.2.13" is an optional dependency and failed compatibility check. Excluding it from installation. [3/4] Linking dependencies... [4/4] Building fresh packages... success Saved lockfile. success Saved 3 new dependencies. info Direct dependencies ├─ react-dom@17.0.2 └─ react@17.0.2 info All dependencies ├─ react-dom@17.0.2 ├─ react@17.0.2 └─ scheduler@0.20.2 Done in 10.41s. xavier@server:~/mib-colors$ cat package.json { "name": "mib-colors", "version": "1.0.0", "main": "index.js", "license": "MIT", "devDependencies": { "@babel/core": "^7.13.14", "@babel/preset-env": "^7.13.12", "@babel/preset-react": "^7.13.13", "babel-loader": "^8.2.2", "css-loader": "^5.2.0", "html-webpack-plugin": "^5.3.1", "style-loader": "^2.0.0", "webpack": "^5.30.0", "webpack-cli": "^4.6.0", "webpack-dev-server": "^3.11.2" }, "dependencies": { "react": "^17.0.2", "react-dom": "^17.0.2" } } |
package.json
On ajoute l’entrée babel
au fichier package.json
(version babel supérieure à 5), et l’entrée scripts
pour lancer la transpilation et le serveur live reload avec yarn
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
{ "name": "mib-colors", "version": "1.0.0", "main": "index.js", "license": "MIT", "author": "Xavier Birnie-Scott <xlebiterrois@gmail.com> (https://murviel-info.com)", "babel": { "presets": [ "@babel/preset-env", "@babel/preset-react" ] }, "scripts": { "start": "webpack serve", "build": "webpack" }, "devDependencies": { "@babel/core": "^7.13.14", "@babel/preset-env": "^7.13.12", "@babel/preset-react": "^7.13.13", "babel-loader": "^8.2.2", "css-loader": "^5.2.0", "html-webpack-plugin": "^5.3.1", "style-loader": "^2.0.0", "webpack": "^5.30.0", "webpack-cli": "^4.6.0", "webpack-dev-server": "^3.11.2" }, "dependencies": { "react": "^17.0.2", "react-dom": "^17.0.2" } } |
Babel transpile l’application et Webpack la lance. L’entrée scripts
définit les actions :
yarn start
: Lance l’application sur le serveur de développement et le live reload,yarn build
: Transpile et construit l’application.
.gitignore
Si l’on utilise git et github pour gérer les versions de l’application, il est important d’exclure certains fichiers et packages de la surveillance du dossier de l’application. En particulier, on ignore le volumineux dossier node_modules
, le dossier dist
et certains fichiers de configuration. Pour cela, on écrit un fichier .gitignore
1 2 3 4 5 6 |
/src-backup /node_modules /dist yarn-error.log .DS_Store .localized |
On complète avec ce que l’on veut exclure. src-backup
est un dossier où je garde des snippets des sources, des idées de codes.
Pour installer l’application avec le dossier node_modules
, il suffira de lancer npm install
, ou yarn install
. Le dossier dist
contient les fichiers exécutables. Pour les reconstruire, on lance alors npm run build
, ou yarn build
, le mieux étant de lire le contenu, la propriété scripts
, du fichier package.json.
Création des fichiers
On crée le répertoire src
, et l’on écrit le fichier index.html
avec une division div
vide destinée à recevoir l’application.
1 2 3 4 5 6 7 8 9 10 11 12 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=devide-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Scratch React App</title> </head> <body> <div id="root"></div> </body> </html> |
Le composant React de l’application prendra place dans la division <div>
avec l’identifiant root
. Le script transpilé de l’application sera automatiquement intégré lors du développement.
On définit le script source de l’application index.js
dans le dossier src
en plaçant le composant React <App />
que l’on définira dans App.js
.
1 2 3 4 5 |
import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; ReactDOM.render(<App />, document.getElementById('root')); |
On crée le composant dans le fichier App.js
, dans le répertoire src
.
1 2 3 4 5 6 7 8 |
import React from 'react'; function App() { return ( <div>Hello World!</div> ); } export default App; |
La page affichera le texte Hello World!.
Configuration Webpack
On crée un fichier de configuration pour Webpack : webpack.config.js
La documentation de ce fichier est accessible sur le site Webpack. On peut s’appuyer également sur la documentation npm
ou github
. Par exemple, pour le plugin html-webpack-plugin
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
const path = require('path'); const HtmlWebPackPlugin = require('html-webpack-plugin'); module.exports = { entry: './src/index.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'index-bundle.js' }, module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: 'babel-loader' }, { test: /\.css$/, use: ['style-loader', 'css-loader'] } ] }, mode: 'development', plugins: [ new HtmlWebPackPlugin({ template: './src/index.html' //source html }) ] } |
On transpile : yarn build
Le fichier transpilé et à intégrer dans index.html
se trouve dans le répertoire dist
du projet et porte le nom index-bundle.js
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
xavier@server:~/mib-colors$ yarn build yarn run v1.22.10 $ webpack asset index-bundle.js 1010 KiB [emitted] (name: main) asset index.html 424 bytes [emitted] runtime modules 670 bytes 3 modules modules by path ./node_modules/ 974 KiB modules by path ./node_modules/scheduler/ 26.3 KiB modules by path ./node_modules/scheduler/*.js 412 bytes 2 modules modules by path ./node_modules/scheduler/cjs/*.js 25.9 KiB 2 modules modules by path ./node_modules/react/ 70.6 KiB ./node_modules/react/index.js 190 bytes [built] [code generated] ./node_modules/react/cjs/react.development.js 70.5 KiB [built] [code generated] modules by path ./node_modules/react-dom/ 875 KiB ./node_modules/react-dom/index.js 1.33 KiB [built] [code generated] ./node_modules/react-dom/cjs/react-dom.development.js 874 KiB [built] [code generated] ./node_modules/object-assign/index.js 2.06 KiB [built] [code generated] modules by path ./src/*.js 320 bytes ./src/index.js 181 bytes [built] [code generated] ./src/App.js 139 bytes [built] [code generated] webpack 5.30.0 compiled successfully in 5070 ms Done in 8.08s. |
L’architecture du dossier de l’application est alors la suivante :
1 2 3 4 5 6 7 8 9 10 11 12 |
mib-colors ├── dist │ ├── index-bundle.js │ └── index.html ├── node_modules ├── package.json ├── src │ ├── App.js │ ├── index.html │ └── index.js ├── webpack.config.js └── yarn.lock |
On lance l’application : yarn start
En cas d’erreur listen EADDRINUSE: address already in use 127.0.0.1:8080, il faut tuer le processus associé, et relancer, voir l’article.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
xavierbs@sd-97133:~/mib-colors$ yarn start yarn run v1.22.10 $ webpack serve ℹ 「wds」: Project is running at http://localhost:8080/ ℹ 「wds」: webpack output is served from / ℹ 「wds」: Content not from webpack is served from /var/www/react/mib-colors ℹ 「wdm」: asset index-bundle.js 1.34 MiB [emitted] (name: main) asset index.html 424 bytes [emitted] runtime modules 1010 bytes 5 modules modules by path ./node_modules/ 1.28 MiB modules by path ./node_modules/webpack-dev-server/ 21.2 KiB 12 modules modules by path ./node_modules/html-entities/lib/*.js 61 KiB 5 modules modules by path ./node_modules/scheduler/ 26.3 KiB 4 modules modules by path ./node_modules/webpack/hot/ 1.58 KiB 3 modules modules by path ./node_modules/querystring/*.js 4.51 KiB 3 modules modules by path ./node_modules/react/ 70.6 KiB 2 modules modules by path ./node_modules/react-dom/ 875 KiB 2 modules modules by path ./node_modules/url/*.js 23.1 KiB ./node_modules/url/url.js 22.8 KiB [built] [code generated] ./node_modules/url/util.js 314 bytes [built] [code generated] modules by path ./src/*.js 320 bytes ./src/index.js 181 bytes [built] [code generated] ./src/App.js 139 bytes [built] [code generated] webpack 5.30.0 compiled successfully in 5643 ms ℹ 「wdm」: Compiled successfully. |
On se connecte en local à l’adresse http://localhost:8080
, les modifications apportées sont prises en compte dans l’application.
Si l’on développe en envoyant les fichiers sur un serveur et que l’on veut s’y connecter de façon externe, on ajoute la propriété devServer
au fichier de configuration Webpack. On définit alors l’adresse IP
du serveur, on peut aussi définir le numéro de port désiré.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
const path = require('path'); const HtmlWebPackPlugin = require('html-webpack-plugin'); module.exports = { entry: './src/index.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'index-bundle.js' }, devServer: { host: 'xxx.xxx.xx.xx', // IP du serveur port: '2802' // N° du port }, module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: 'babel-loader' }, { test: /\.css$/, use: ['style-loader', 'css-loader'] } ] }, mode: 'development', plugins: [ new HtmlWebPackPlugin({ template: './src/index.html' //source html }) ] } |
Et l’on relance le serveur : yarn start
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
xavierbs@sd-97133:~/www/_murviel-info/react/mib-colors$ yarn start yarn run v1.22.10 $ webpack serve ℹ 「wds」: Project is running at http://xxx.xxx.xx.xx:2802/ ℹ 「wds」: webpack output is served from / ℹ 「wds」: Content not from webpack is served from ~/mib-colors ℹ 「wdm」: asset index-bundle.js 1.34 MiB [emitted] (name: main) asset index.html 424 bytes [emitted] runtime modules 1010 bytes 5 modules modules by path ./node_modules/ 1.28 MiB modules by path ./node_modules/webpack-dev-server/ 21.2 KiB 12 modules modules by path ./node_modules/html-entities/lib/*.js 61 KiB 5 modules modules by path ./node_modules/scheduler/ 26.3 KiB 4 modules modules by path ./node_modules/webpack/hot/ 1.58 KiB 3 modules modules by path ./node_modules/querystring/*.js 4.51 KiB 3 modules modules by path ./node_modules/react/ 70.6 KiB 2 modules modules by path ./node_modules/react-dom/ 875 KiB 2 modules modules by path ./node_modules/url/*.js 23.1 KiB ./node_modules/url/url.js 22.8 KiB [built] [code generated] ./node_modules/url/util.js 314 bytes [built] [code generated] modules by path ./src/*.js 320 bytes ./src/index.js 181 bytes [built] [code generated] ./src/App.js 139 bytes [built] [code generated] webpack 5.30.0 compiled successfully in 5947 ms ℹ 「wdm」: Compiled successfully. ℹ 「wdm」: Compiling... ℹ 「wdm」: asset index-bundle.js 1.34 MiB [emitted] (name: main) asset index.html 424 bytes [emitted] cached modules 1.28 MiB [cached] 40 modules runtime modules 1010 bytes 5 modules ./src/App.js 139 bytes [built] [code generated] webpack 5.30.0 compiled successfully in 318 ms ℹ 「wdm」: Compiled successfully. |
On se connecte dans un navigateur à l’adresse http://xxx.xxx.xx.xx:2802
Les modifications de l’application s’effectuent alors en temps réel.
Pour arrêter le watcher (live reload), on utilise le raccourci Ctrl+C
.
Pour créer le fichier final pour la production, il faudra enlever la propriété devServer
et changer le mode
à production
dans le fichier de configuration Webpack.
Et hop, on peut développer des applications React sans create-react-app
, en approfondissant en même temps l’utilisation de Webpack.
L’utilisation d’un composant externe et l’utilisation de cet environnement est présenté dans l’article suivant.