Comment créer une application React à partir de zéro à l'aide de Webpack 4

Depuis trois semaines, j'essaie de créer une application React à partir de zéro pour comprendre la configuration avec Webpack. Mon objectif était de mettre en place une configuration simple qui pourra ensuite être développée. Cela a été une lutte pour comprendre Webpack. Mais grâce à ce tutoriel de Valentino Gagliardi, je suis très éclairé.

Ce que je prévois de faire, c'est de créer une fonctionnalité de recherche avec de fausses données JSON (ou réelles). Dans ce billet de blog, je vais parcourir la mise en place de mon projet. Dans le prochain, je prévois de montrer comment configurer les tests. Je voudrais également ajouter un serveur à cela en utilisant Node.js, mais je ne sais pas si la portée de mon projet en aurait besoin.

( Remarque : j'ai fourni ma configuration Webpack à la fin de ce billet de blog)

Sans plus tarder, passons à la configuration!

Créez un nouveau projet et ajoutez -y un cd :

mkdir react_searchcd react_search

Créez un fichier package.json :

npm init

Si vous souhaitez ignorer toutes les questions, ajoutez l'indicateur -y:

npm init -y

Nous devons installer webpack en tant que dépendance dev et webpack-cli afin que vous puissiez utiliser webpack dans la ligne de commande:

npm i webpack webpack-cli -D
  • i: installer
  • -D: - save-dev

Créez un dossier src avec index.js et placez le code suivant comme exemple:

console.log("hello");

Ajoutez maintenant les scripts suivants à votre package.json (en gras):

{ "name": "react_search", "version": "1.0.0", "description": "Search app using React", "main": "index.js", "scripts": { "start": "webpack --mode development", "build": "webpack --mode production" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "webpack": "^4.0.1", "webpack-cli": "^2.0.10" } }

Webpack 4 dispose désormais de deux modes, le développement et la production où le code est minimisé dans ce dernier.

Voyez-le par vous-même en exécutant:

npm run start

Cela créera un dossier dist avec le fichier main.js à l'intérieur (contenant votre code src).

Si vous exécutez maintenant:

npm run build

La sortie suivante est maintenant comme ceci:

Configurer React et Babel

Pour travailler avec React, nous devons l'installer avec Babel. Cela transpilera le code de ES6 à ES5, car tous les navigateurs ne prennent pas encore en charge ES6 (par exemple Internet Explorer).

Installez react et react-dom en tant que dépendance

npm i react react-dom -S
  • -S: - enregistrer

Ensuite, installez babel-core , babel-loader , babel-preset-env et babel-preset-react en tant que dépendance dev:

npm i babel-core babel-loader babel-preset-env babel-preset-react -D
  • babel-core : transforme votre code ES6 en ES5
  • babel-loader : assistant Webpack pour transformer vos dépendances JavaScript (par exemple, lorsque vous importez vos composants dans d'autres composants) avec Babel
  • babel-preset-env : détermine les transformations / plugins à utiliser et les polyfills (fournissent des fonctionnalités modernes sur les navigateurs plus anciens qui ne le prennent pas en charge nativement) en fonction de la matrice de navigateur que vous souhaitez prendre en charge
  • babel-preset-react : preset Babel pour tous les plugins React, par exemple transformer JSX en fonctions

Nous devons créer un fichier webpack.config.js pour énoncer les règles de notre babel-loader.

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

Nous devons ensuite créer un fichier séparé appelé .babelrc pour fournir les options de babel-loader. Vous pouvez l'inclure dans le fichier webpack.config.js, mais j'ai vu que la plupart des projets l'ont séparé. Cela se traduit par une lisibilité plus claire et peut être utilisé par d'autres outils sans rapport avec Webpack. Lorsque vous déclarez que vous utilisez babel-loader dans votre configuration webpack, il recherchera le fichier .babelrc s'il y en a un.

{ "presets": ["env", "react"] }

Ensuite, modifiez votre fichier index.js pour rendre un composant:

import React from "react"; import ReactDOM from "react-dom"; const Index = () => { return Hello React! ; }; ReactDOM.render(, document.getElementById("index"));

Nous devrons également créer un fichier index.html dans le dossier src où nous pouvons ajouter notre élément de section avec l'id index. C'est ici que nous rendons notre principal composant de réaction:

      React and Webpack4     

Nous devons maintenant installer html-webpack-plugin et l'utiliser dans notre fichier de configuration webpack. Ce plugin génère un fichier HTML avec injecté, écrit le fichier dans dist / index .html et réduit le fichier.

Installez html-webpack-plugin en tant que dépendance de développement:

npm i html-webpack-plugin -D

Mettez à jour la configuration du webpack comme ceci:

const HtmlWebPackPlugin = require("html-webpack-plugin"); const htmlPlugin = new HtmlWebPackPlugin({ template: "./src/index.html", filename: "./index.html" }); module.exports = { module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: { loader: "babel-loader" } } ] }, plugins: [htmlPlugin] };

Vous pouvez également saisir le plugin comme ceci:

plugins: [ new HtmlWebPackPlugin({ template: "./src/index.html", filename: "./index.html" }); ]

Mais je préfère extraire ceci dans une variable pour que je puisse voir la liste des plugins que j'utilise.

La valeur que je donne à la templateclé est l'endroit où je recherche mon fichier HTML. La valeur du nom de fichier est le nom du HTML minifié qui sera généré dans le dossier dist.

Si vous exécutez maintenant, npm run startvous devriez voir index.html généré dans le dossier dist.

Run open dist/index.html and you should see “Hello React” in your browser.

Setting up webpack-dev-server

It is a bit tedious to keep running this command every time you want to see your changes in the browser. To have webpack “watch” our changes and thus refresh whenever we have made changes to any of our components, we can use webpack-dev-server module.

Go ahead and install this as a dev dependency

npm i webpack-dev-server -D

Then change your package.json start scripts like so (in bold):

{ "name": "react_search", "version": "1.0.0", "description": "Search app using React", "main": "index.js", "scripts": { "start": "webpack-dev-server --mode development --open", "build": "webpack --mode production" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "react": "^16.2.0", "react-dom": "^16.2.0" "devDependencies": { "babel-core": "^6.26.0", "babel-loader": "^7.1.4", "babel-preset-env": "^1.6.1", "babel-preset-react": "^6.24.1", "html-webpack-plugin": "^3.0.6", "webpack": "^4.1.1", "webpack-cli": "^2.0.10", "webpack-dev-server": "^3.1.0" } }

If you now run npm run start you should see localhost:8080 open up in your default browser — that’s what the —-open flag is for. Now everytime you make changes, it will refresh the page.

You can also add a --hot flag to your npm start script which will allow you to only reload the component that you’ve changed instead of doing a full page reload. This is Hot Module Replacement.

Setting up CSS

The last part involves setting up our CSS. As we will be importing CSS files into our React components, we need css-loader module to resolve them. Once that’s resolved, we also need a style-loader to inject this into our DOM — adding a tag into the element of our HTML.

Go ahead and install both of these modules as a dev dependency:

npm i css-loader style-loader -D

We then need to update our webpack.config.js file like so:

const HtmlWebPackPlugin = require("html-webpack-plugin"); const htmlWebpackPlugin = new HtmlWebPackPlugin({ template: "./src/index.html", filename: "./index.html" }); module.exports = { module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: { loader: "babel-loader" } }, { test: /\.css$/, use: ["style-loader", "css-loader"] } ] }, plugins: [htmlWebpackPlugin] };

Note that the order of adding these loaders is important. First, we need to resolve the CSS files before adding them to the DOM with the style-loader. By default, webpack uses the loaders from the right (last element in the array) to the left (first element in the array).

Making CSS modular

We can also make CSS modular using webpack. This means class name will be scoped locally and specific to only the component in question.

To do this, we can provide some options to css-loader:

const HtmlWebPackPlugin = require("html-webpack-plugin"); const htmlWebpackPlugin = new HtmlWebPackPlugin({ template: "./src/index.html", filename: "./index.html" }); module.exports = { module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: { loader: "babel-loader" } }, { test: /\.css$/, use: [ { loader: "style-loader" }, { loader: "css-loader", options: { modules: true, importLoaders: 1, localIdentName: "[name]_[local]_[hash:base64]", sourceMap: true, minimize: true } } ] } ] }, plugins: [htmlWebpackPlugin] };

As we need to give options, each loader is now an object with a key-value pair. To enable CSS modules, we need to set module option for css-loader to be true. The importLoaders option configures how many loaders before css-loader should be applied. For example, sass-loader would have to come before css-loader.

The localIdentName allows you to configure the generated identification.

  • [name] will take the name of your component
  • [local] is the name of your class/id
  • [hash:base64] is the randomly generated hash which will be unique in every component’s CSS

To make this a bit more visual, I’ll give you an example. Say I have a component named Form and I have a button with a CSS class primaryButton. I also have another component called Search and a button in it with a CSS class primaryButton. However, both of these classes have different CSS:

Form button.primaryButton { background-color: green; } Search button.primaryButton { background-color: blue; }

When webpack bundles your application, depending on which CSS comes latest, both of your buttons could have the color green or blue instead of Form having green and Search having blue.

This is where the localIdentName comes into place. With this, once your application is bundled, your buttons will have a unique class name!

As you can see, the button class name in the Form component is different to the one in the Search component — their naming starts with the name of the component, class name, and unique hash code.

So with this, you won’t have to worry about whether you have given the same class name throughout your whole application — you only have to worry about whether you have used it in the same component.

This concludes the first part of setting a React app from scratch. In the next blog post, I aim to explain how to set up tests for TDD and how to write them.

Please let me know if something is unclear and I’ll explain the best as I can. I value and welcome constructive feedback as this helps me to improve :)

Hope this helps!

EDIT

Importing CSS

I’ve had a few comments asking me how they can render CSS which I didn’t touch on previously. What you need to do is import the CSS file in your React component. For example, say you have a Search component and this is your tree directory:

You will need to import your CSS file in your Search component like so:

import style from "./Search.css"

You can then apply different CSS class styles such as:

const Search = () => { return Hello Search Component :) }

You don’t have to call it style but what I found is that most people have given it this name in their projects.

My Webpack boilerplate

For anyone who wants a quick clone of this Webpack setup, I have this on my GitHub. I’ve also included a more succinct guide in the README.

Entry and output points

Webpack 4 by default has a default entry point of index.js in your src folder. If you would like to point to a different file, you can do so by specifying an entry point in your webpack config file:

e.g.

module.exports = { entry: "./src/app.js", module: { ... } }

You can also specify output file like so:

const path = require('path') module.exports = { entry: "./src/app.js", output: { path: path.resolve(‘dist’), filename: ‘bundled.js’ }, module: { ... } }

Thanks to Gudu Kassa for pointing this out!

If you have found this helpful please share it on social media :)

www.pinglinh.com

Follow me on Twitter | Check out my LinkedIn | See my GitHub