Cómo crear una aplicación React desde cero usando Webpack 4

Durante las últimas tres semanas, he intentado crear una aplicación React desde cero para comprender la configuración con Webpack. Mi objetivo era establecer una configuración simple que luego se pueda desarrollar. Ha sido una lucha comprender Webpack. Pero gracias a este tutorial de Valentino Gagliardi, estoy muy iluminado.

Lo que planeo hacer es crear una funcionalidad de búsqueda con algunos datos JSON falsos (o reales). En esta publicación de blog, revisaré la configuración de mi proyecto. En el siguiente, planeo mostrar cómo configurar las pruebas. También me gustaría agregar un servidor a esto usando Node.js, pero no estoy seguro de si el alcance de mi proyecto lo necesitaría.

( Nota : proporcioné mi configuración de Webpack al final de esta publicación de blog)

Sin más preámbulos, ¡sigamos con la configuración!

Haga un nuevo proyecto y cd en él:

mkdir react_searchcd react_search

Cree un archivo package.json :

npm init

Si desea omitir todas las preguntas, agregue la marca -y:

npm init -y

Necesitamos instalar webpack como una dependencia de desarrollo y webpack-cli para que pueda usar webpack en la línea de comando:

npm i webpack webpack-cli -D
  • yo: instalar
  • -D: - guardar-dev

Cree una carpeta src con index.js y coloque el siguiente código como ejemplo:

console.log("hello");

Ahora agregue los siguientes scripts a su package.json (en negrita):

{ "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 ahora tiene dos modos, desarrollo y producción donde el código se minimiza en este último.

Véalo usted mismo ejecutando:

npm run start

Esto creará una carpeta dist con el archivo main.js dentro (que contiene su código src).

Si ahora ejecuta:

npm run build

La siguiente salida ahora es así:

Configuración de React y Babel

Para trabajar con React, necesitamos instalarlo junto con Babel. Esto transpilará el código de ES6 a ES5, ya que no todos los navegadores son compatibles con ES6 todavía (por ejemplo, Internet Explorer).

Instale react y react-dom como una dependencia

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

Luego instale babel-core , babel-loader , babel-preset-env y babel-preset-react como una dependencia de desarrollo:

npm i babel-core babel-loader babel-preset-env babel-preset-react -D
  • babel-core : transforma su código ES6 en ES5
  • babel-loader : ayuda de Webpack para transformar sus dependencias de JavaScript (por ejemplo, cuando importa sus componentes a otros componentes) con Babel
  • babel-preset-env : Determina qué transformaciones / complementos usar y polyfills (brindan funcionalidad moderna en navegadores más antiguos que no lo admiten de forma nativa) según la matriz del navegador que desea admitir
  • babel-preset-react : Babel preset para todos los complementos de React, por ejemplo, convertir JSX en funciones

Necesitamos crear un archivo webpack.config.js para establecer las reglas de nuestro babel-loader.

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

Luego necesitamos crear un archivo separado llamado .babelrc para proporcionar las opciones de babel-loader. Puede incluirlo en el archivo webpack.config.js, pero he visto que la mayoría de los proyectos tienen esto separado. Esto da como resultado una legibilidad más clara y puede ser utilizado por otras herramientas no relacionadas con el paquete web. Cuando declara que está usando babel-loader en la configuración de su paquete web, buscará el archivo .babelrc si lo hay.

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

A continuación, cambie su archivo index.js para representar un componente:

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

También necesitaremos crear un archivo index.html en la carpeta src donde podemos agregar nuestro elemento de sección con id index. Aquí es donde renderizamos nuestro componente principal de reacción:

      React and Webpack4     

Ahora necesitamos instalar html-webpack-plugin y usarlo en nuestro archivo de configuración de paquete web. Este complemento genera un archivo HTML con inyectado, escribe esto en dist / index .html y minimiza el archivo.

Instale html-webpack-plugin como dependencia de desarrollo:

npm i html-webpack-plugin -D

Actualice la configuración del paquete web así:

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

También puede ingresar el complemento de esta manera:

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

Pero prefiero extraer esto en una variable para poder ver la lista de complementos que estoy usando.

El valor que le doy a la templateclave es donde estoy buscando mi archivo HTML. El valor del nombre de archivo es el nombre del HTML minimizado que se generará en la carpeta dist.

Si ahora ejecuta npm run start, debería ver index.html que se genera en la carpeta 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