Entorno de desarrollo para React.js

12 de marzo 2019ComentariosjavascriptDavid Poza SuárezComentarios

Tras varios meses muy volcado con React.js, me gustaría escribir un primer tutorial sobre el tema, y como no podía ser de otro modo vamos a ver la configuración del entorno de desarrollo. Aunque si has llegado hasta aquí ya lo sabes: me gustan las definiciones jeje:

React.js es una librería (que no un framework, como lo es Angular), creada por Facebook con el objetivo de crear user interfaces para SPA(single page application) de alto rendimiento. Al ser tan solo una librería, necesita apoyarse en otras tantas, dando la posibilidad de elegir cuáles y de incluir únicamente aquellas que necesitamos. Como para todo en la vida, hay gustos, esta flexibilidad puede ser vista como algo positivo o como algo negativo. Pero el hecho es una de las tres herramientas que copan el mercado del desarrollo web en la actualidad, junto a Angular y VueJs.

Antes de nada hay que tener claro qué son los siguientes software:

  • NodeJS: Es un entorno en tiempo de ejecución multiplataforma, de código abierto, basado en el lenguaje de programación ECMAScript, a su vez basado en el motor V8 de Google.
  • NPM: Es el gestor de paquetes por defecto para Node.js. Similar a PIP para Python o Composer para PHP. Existen otros gestores de paquetes para node como Yarn. Nos centraremos en npm ya que es el que viene instalado.
  • Babel: Como ya decíamos aquí, se trata de un transpilador para poder escribir código ES6 o superior y convertirlo automáticamente en ES5, que entienden todos los navegadores. Su configuración se localiza en el fichero .babelrc, que contiene un objeto json.
  • Webpack: Es un bundler, o agrupador, que permite generar ficheros de salida a partir de diferentes ficheros entrada, aplicando diferentes transformaciones según los loaders y plugins que le podemos ir instalando. Se usa muchísimo en Javascript, para proyectos React.js por ejemplo, disponiendo de loaders como babel, o también loaders propios para react o SASS. Su configuración se contiene en el fichero webpack.config.js.
  • create-react-app: Es una utilidad también creada por Facebook para ahorrarnos todo lo que voy a explicar en este artículo usando un solo comando. Obviamente, yo te recomiendo, siempre dentro lo posible, conocer qué está sucediendo por debajo, así obtendrás toda la potencia de las herramientas y será así como enfrentarás proyectos reales.

Instalar Node.js

En Windows , nada más sencillo que acudir a la web https://nodejs.org/es/ y descargar el setup en su versión LTS.

Para Ubuntu simplemente añadimos un ppa para poder descargar la última LTS aunque no uses la versión.

sudo apt install curl curl -sL https://deb.nodesource.com/setup\_10.x | sudo bash - sudo apt install nodejs Puedes comprobar la versión de node con node -v Y la versión de npm con npm -v.

Crear el proyecto vacío

Usando npm init crearemos un directorio de proyecto con un fichero package.json con toda la configuración de dependencias, pudiendo a partir de éste desplegar el proyecto en una máquina desde cero. Así nos evitamos subir todas las dependencias al repositorio del proyecto, por ejemplo.

Un package de un proyecto vacío pinta así:

{ "name": "proyecto", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" }

Las dependencias se irán añadiendo automáticamente cuando instalamos los paquetes con npm, tal y como indicaré después.

El otro cambio importante es la propiedad "scripts" que contiene un objeto con diferentes propiedades para las acciones que queremos disparar mediante "npm run-script accion", es típico establecer un start para desplegar un servidor de desarrollo y build compilar el fichero de proyecto para producción. Es aquí donde llamamos al comando webpack y webpack-server-dev.

///Ejemplo de package.json //////////////////////////////////////// { "name": "dps-toggl", "version": "0.0.1", "description": "my own Toggl version in React.js", "main": "index.js", "scripts": { "start": "webpack-dev-server --hot --inline --colors --progress", "build": "webpack -p --colors" }, "repository": { "type": "git", "url": "git+https://github.com/davidpoza/dps-toggl.git" }, "author": "David Poza Suárez", "license": "ISC", "bugs": { "url": "https://github.com/davidpoza/dps-toggl/issues" }, "homepage": "https://github.com/davidpoza/dps-toggl#readme", "dependencies": { "file-loader": "^3.0.1", "react": "^16.8.4", "react-dom": "^16.8.4" }, "devDependencies": { "@babel/core": "^7.3.4", "@babel/preset-env": "^7.3.4", "@babel/preset-react": "^7.0.0", "babel-loader": "^8.0.5", "css-loader": "^2.1.1", "mini-css-extract-plugin": "^0.5.0", "node-sass": "^4.11.0", "react-hot-loader": "^4.8.0", "sass-loader": "^7.1.0", "style-loader": "^0.23.1", "webpack": "^4.29.6", "webpack-cli": "^3.2.3", "webpack-dev-server": "^3.2.1" } }

Instalar paquetes

De forma automática npm irá editando el package.json añadiendo las dependencias en la propiedad "dependencies" y "devDependencies" según corresponda.

//Instalamos las librerías de producción de forma local (dentro del directorio del proyecto en la carpeta node_modules) npm install -S react react-dom

//Instalamos las librerías de desarrollo, que no se incluyen en un despliegue de producción npm install -D webpack webpack-cli babel-loader @babel/core @babel/preset-env @babel/preset-react webpack-dev-server react-hot-loader node-sass style-loader css-loader sass-loader extract-text-webpack-plugin

Detalle de cada paquete:

  • webpack: el bundler como hemos dicho antes
  • webpack-cli: los comandos de consola que llaman a webpack
  • babel-loader: el loader que permite llamar a babel desde webpack
  • @babel/core: el paquete principal de babel
  • @babel/preset-env: el preset de babel para convertir de ES moderno a ES5
  • @babel/preset-react: el preset de babel para convertir JSX de React a HTML
  • webpack-dev-server: servidor de desarrollo
  • react-hot-loader: nos permite refrescar automáticamente el navegador cuando hacemos algún cambio en el código fuente.
  • node-sass: el compilador de SASS.
  • todos los que incluyen la palabra loader: son loaders de webpack que explicaré a continuación.
  • MiniCssExtractPlugin: Este no es un loader sino un plugin, y sustituye al extract-text-webpack a partir de webpack v4. Lo vamos a usar para unir todos los ficheros scss en uno solo.

Configurar babel

Simplemente creamos el fichero .babelrc en la raíz del proyecto. En él vamos a indicar las traducciones que queremos que Babel sea capaz de realizar. En este caso queremos que convierta de ECMAScript última versión a ES5 y de JSX (de React.js) a html. Por lo que le vamos a indicar el siguiente objeto json:

{ "presets": ["@babel/preset-env", "@babel/preset-react"] }

Configurar Webpack

Aquí es donde mucha gente se asusta, yo el primero cuando lo ví por primera vez. Pero se trata de un fichero js que al final contiene un módulo exportado y éste es muy intuitivo.

Antes de nada, he decidido que la estructura de mi proyecto sea la siguiente:

estructura proyecto react

Colocaremos un index.html en la raíz del directorio de salida para que cuando ejecutemos el servidor de prueba sea este fichero el que se muestre. Simplemente contendrá el nodo principal (donde cargamos el componente React principal) y la inclusión del fichero javascript de salida, en nuestro caso bundle.js

dpsToggl
Cargando...

Loaders Webpack

Es en el array module.rules donde vamos a ir indicando los loaders que aplican para cada extensión de fichero.

El orden en que webpack aplica los loaders es del último al primero.

En un proyecto react una lista de los más usados sería:

  • babel-loader (para los .js): Convertirá el ECMAScript en su última versión al estándar ES5
  • css-loader: Recolecta todos los css referenciados en la aplicación y los combina en un único string.

    • modules: Nos habilita CSS MODULES, que nos permite tener un ámbito de nombres css local a cada componente. Por lo que no tendremos que preocuparnos por repetir nombres de clases.
    • localIdentName: Es el patrón que van a seguir los nombres de los identificadores de estilos, para poder diferenciarse cuando carguen todos juntas en la misma página. Se indica el nombre del componente que será útil para depurar, el nombre de identificador y al final añadimos un hash de longitud 5 que nos garantiza que nunca van a coincidir dos nombres de clases iguales entre distintos componentes.
  • sass-loader: Este loader requiere además tener instalado el paquete node-sass (npm install -S node-sass). Convierte ficheros sass a css.
  • file-loader: Nos permite importar assets al proyecto, como por ejemplo imágenes, tal y como si fueran objetos: import icon from '../images/icon.png';

Recordad que la sintaxis del fichero de configuración va cambiando con cierta frecuencia. Para estar al tanto de las propiedades que van quedando obsoletas, revisa su documentación oficial. Ahora el fichero webpack.config.js podría tener la siguiente pinta:

//Vamos a usar el paquete path para que las rutas sean válidas tanto en windows como en linux var path = require("path"); var entryPath = path.join(__dirname, "src"), outPath = path.join(__dirname, "dist");

//Vamos a usar este plugin para combinar todos los ficheros scss const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = { entry: [path.join(entryPath, "app.js")], output: { path: outPath, filename: "bundle.js" }, module: { rules : [ { test: /\.js$/, include: entryPath, exclude: path.join(__dirname, "node_modules"), use: [ { loader: 'babel-loader', }, { loader: 'react-hot-loader/webpack', }, ] }, { test: /\.scss$/, include: entryPath, use: [ MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { modules: true, sourceMap: true, localIdentName: '[name]__[local]__[hash:base64:5]' } }, { loader: 'sass-loader' } ] }, { test: /\.(png|jpg|gif)$/, include: entryPath, use: [ { loader: 'file-loader', options: { outputPath: 'images/', publicPath: 'images/', }, }, ], } ] }, devServer: { contentBase: outPath, port: 7777, //si queremos cambiar el puerto 8080 que viene por defecto host: '0.0.0.0', //si deseamos que el servidor de desarrollo sea accesible desde cualquier equipo de la red }, plugins: [ new MiniCssExtractPlugin({ filename: "styles.css", chunkFilename: "styles.css" }), ] }

Despliegue y compilación

Gracias a los scripts configurados en el package.json, podemos hacer dos cosas:

  • Lanzar el servidor de desarrollo con con los cambios en caliente habilitados: npm start
  • O bien compilar una versión de producción del código: npm run-script build

Hola Mundo con SASS y CSS Modules

Aunque no es el objetivo de este post empezar a crear componentes, vamos a mostrar un ejemplo hola mundo, usando dos componentes padre hijo, y scss junto a css-modules para darles estilo, y veremos como en el fichero de salida únicamente se enlaza a un css con el sass ya interpretado.

dpsToggl
Cargando...

import React from 'react'; import ReactDOM from 'react-dom'; import AppComponent from './components/AppComponent/AppComponent';

ReactDOM.render(, document.getElementById("app"));

$primary-color: red;

import React, {Component} from 'react' import icon from '../../images/icon.png'; import styles from './AppComponent.scss'; import ChildComponent from '../ChildComponent/ChildComponent';

class AppComponent extends Component{ constructor(props){ super(props); } render(){ return(

Funciona!
) } }

export default AppComponent;

.clase{ color: green; }

import React, {Component} from 'react' import styles from './ChildComponent.scss';

class ChildComponent extends Component{ constructor(props){ super(props); } render(){ return(

Hijo
) } }

export default ChildComponent;

@import "../../defaults.scss";

.clase{ color: $primary-color; }

Salida

dpsToggl
Cargando...

.AppComponent__clase__3UJff { color: green; }

.ChildComponent__clase__1j_Gf { color: red; }