So erstellen Sie eine React-App mit Webpack 4 von Grund auf neu

In den letzten drei Wochen habe ich versucht, eine React-App von Grund auf neu zu erstellen, um die Einrichtung mit Webpack zu verstehen. Mein Ziel war es, eine einfache Konfiguration einzurichten, auf die man dann aufbauen kann. Es war ein Kampf, Webpack zu verstehen. Aber dank dieses Tutorials von Valentino Gagliardi bin ich sehr aufgeklärt.

Was ich vorhabe, ist eine Suchfunktion mit einigen gefälschten JSON-Daten (oder echten). In diesem Blog-Beitrag werde ich die Einrichtung meines Projekts durchgehen. Im nächsten habe ich vor zu zeigen, wie man Tests einrichtet. Ich möchte diesem auch einen Server mit Node.js hinzufügen, bin mir aber nicht sicher, ob der Umfang meines Projekts dies erfordern würde.

( Hinweis : Ich habe mein Webpack-Setup am Ende dieses Blog-Beitrags bereitgestellt.)

Lassen Sie uns ohne weiteres mit dem Setup fortfahren!

Machen Sie ein neues Projekt und CD darin:

mkdir react_searchcd react_search

Erstellen Sie eine package.json- Datei:

npm init

Wenn Sie alle Fragen überspringen möchten, fügen Sie das Flag -y hinzu:

npm init -y

Wir müssen webpack als dev-Abhängigkeit und webpack-cli installieren, damit Sie webpack in der Befehlszeile verwenden können:

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

Erstellen Sie einen src-Ordner mit index.js und geben Sie den folgenden Code als Beispiel ein:

console.log("hello");

Fügen Sie nun die folgenden Skripte zu package.json hinzu (fett gedruckt):

{ "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 verfügt nun über zwei Modi: Entwicklung und Produktion, bei denen der Code in letzterem minimiert wird.

Überzeugen Sie sich selbst, indem Sie Folgendes ausführen:

npm run start

Dadurch wird ein dist- Ordner mit der Datei main.js erstellt (die Ihren src-Code enthält).

Wenn Sie jetzt laufen:

npm run build

Die folgende Ausgabe sieht jetzt so aus:

Reagieren und Babel einrichten

Um mit React arbeiten zu können, müssen wir es zusammen mit Babel installieren. Dadurch wird der Code von ES6 auf ES5 übertragen, da noch nicht alle Browser ES6 unterstützen (z. B. Internet Explorer).

Installieren Sie react und react-dom als Abhängigkeit

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

Installieren Sie dann Babel-Core , Babel-Loader , Babel-Preset-Env und Babel-Preset-React als Entwicklungsabhängigkeit:

npm i babel-core babel-loader babel-preset-env babel-preset-react -D
  • babel-core : Wandelt Ihren ES6-Code in ES5 um
  • babel-loader : Webpack-Helfer zum Transformieren Ihrer JavaScript-Abhängigkeiten (z. B. beim Importieren Ihrer Komponenten in andere Komponenten) mit Babel
  • babel-preset-env : Legt basierend auf der zu unterstützenden Browsermatrix fest, welche Transformationen / Plugins verwendet und welche Polyfills (bieten moderne Funktionen in älteren Browsern, die diese nicht nativ unterstützen)
  • babel-preset-react : Babel-Voreinstellung für alle React-Plugins, z. B. Umwandlung von JSX in Funktionen

Wir müssen eine webpack.config.js- Datei erstellen, um die Regeln für unseren Babel-Loader anzugeben .

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

Wir müssen dann eine separate Datei namens .babelrc erstellen , um die Optionen für den Babel-Loader bereitzustellen. Sie können es in die Datei webpack.config.js aufnehmen, aber ich habe gesehen, dass die meisten Projekte dies getrennt haben. Dies führt zu einer besseren Lesbarkeit und kann von anderen Tools verwendet werden, die nichts mit Webpack zu tun haben. Wenn Sie angeben, dass Sie babel-loader in Ihrer Webpack-Konfiguration verwenden, sucht es nach einer .babelrc-Datei, falls vorhanden.

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

Ändern Sie als Nächstes Ihre Datei index.js , um eine Komponente zu rendern:

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

Wir müssen auch eine index.html- Datei im Ordner src erstellen, in der wir unser Abschnittselement mit der ID hinzufügen können index. Hier rendern wir unsere Hauptreaktionskomponente:

      React and Webpack4     

Jetzt müssen wir das HTML-Webpack-Plugin installieren und dieses in unserer Webpack-Konfigurationsdatei verwenden. Dieses Plugin generiert eine HTML-Datei mit injiziert, schreibt diese in dist / index .html und minimiert die Datei.

Installieren Sie das HTML-Webpack-Plugin als Entwicklungsabhängigkeit :

npm i html-webpack-plugin -D

Aktualisieren Sie die Webpack-Konfiguration wie folgt:

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] };

Sie können das Plugin auch folgendermaßen eingeben:

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

Ich ziehe es jedoch vor, dies in eine Variable zu extrahieren, damit ich die Liste der von mir verwendeten Plugins sehen kann.

Der Wert, den ich dem templateSchlüssel gebe, ist der Ort, an dem ich nach meiner HTML-Datei suche. Der Dateinamenwert ist der Name des minimierten HTML-Codes, der im dist-Ordner generiert wird.

Wenn Sie jetzt ausführen npm run start, sollte index.html im Ordner dist generiert werden.

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