Deploy: https://jlaguilargomez.github.io/robofriends-app/
El objetivo de este módulo es integrar REDUX en la aplicación realizada con REACT que tiene la compañía. Así mismo se nos pide optimizar y revisar la aplicación.
La incremental complejidad de las páginas webs y aplicaciones, ha hecho que el código JS crezca de una forma exponencial. Para posibilitar el mantenimiento a largo plazo así como la reducción de bugs y/o detección y eliminación de los mismos, nacen los "frameworks" y las "librerías".
Debemos valorar PROS y CONTRAS de cada framework / librería a la hora de llevar a cabo una aplicación. Ninguna es mejor o peor y es el mercado y las necesidades de producto las que deben determinar su uso.
En el curso se pone un ejemplo de Angular como una cocina con todas las herramientas disponibles, se considera a React como una herramienta muy flexible y con gran facilidad de adaptación y a Vue una herramienta inicial perfecta (ten en cuenta que el video es de JUL 17, las cosas han cambiado mucho desde entonces)
Pensemos por un momento en la gestión de componentes por parte de React y comparemoslo con la bilogía molecular de los seres vivos.
Hay que recordar también que el flujo de datos en React va siempre "aguas abajo", es decir, sólo los componentes hijos son conscientes de cambios en el componente padre, no a la inversa, como vemos en el dibujo siguiente:
Otra de las características más importantes de React es el manejo del DOM virtual. Ya no somos nosotros el "pintor" que manipula el DOM (cuanto menos manipulación directa, mejor para el rendimiento), ahora es una especie de "bot" de React el que recibe la estructura y composición del DOM mediante un objeto de JS y se encarga de introducir y realizar los cambios de la forma más óptima posible.
Son 3 los vídeos que nos introducen a este tema en el curso, pero es el 3 el que nos importa, ya que es el más actualizado.
Ya no es necesario instalar la dependencia global "create-react-app", ya que podemos inicializar la aplicación directamente mediante NPX con el comando:
npx create-react-app APPNAME
Se instalan los "package" y las librerías necesarias:
How to configure ESLint and Prettier in React
Utilizamos como librería de CSS 'Tachyons'. No tengo ni idea de cómo es ni reseñas de la misma. Vamos a probarla ...
Recuerda la utilizad de los props en React:
ReactDom.render(
<Hello greeting="{'Hello Peeedro}" />,
document.getElementById('root')
);
Si utilizamos la notación de funciones en lugar de la de clases, podemos pasarle los "props" como argumentos de la función:
Utilizamos como referencia para las imagenes la API siguiente, que nos permite generar imagenes aleatorias de robots:
Usamos 'destructuring' para transformar las propiedades del objeto "props" en las variables que queremos, así es más "accesible":
Como se trata de un array de datos (la lista de robots), vamos a iterar en la misma para crear una por cada elemento de dicho array.
Creamos un nuevo componente (CardList) e iteramos en el mismo mediante el método "map":
OJO: Cuando generamos un listado de elementos para ser renderizado en el DOM, React exige (con razón), que demos una ID única a cada uno de dichos elementos para poder identificarlos en caso necesario. En este caso lo hacemos mediante key={robot.id} que sabemos que es un identificador único.
** A 02/11/20 hay una nueva versión de "create-react-app":
Getting Started | Create React App
Recordemos que para estilos estoy usando la librería "Tachyons":
Y así puedo dar algo de estilo a la aplicación sin tener que gastar mucho tiempo en ello:
<div class="pa2">
<input
class="pa3 ba b--greeen b-lighttest-blue"
type="search"
placeholder="search robots"
/>
</div>
Estamos en este punto:
Para establecer una comunicación entre el componente SearchBar
y el CardList
, mediante la comunicación "One Way Data Flow" debemos pasar por AppComponent
Sería un tipo de comunicación entre "hermanos?"
El componente CardList
es un componente "puro" ya que recibe siempre un array de "robots" (PROPS) y SIEMPRE devuelve lo mismo.
No necesita saber ni hacer nada más.
Entra ahora en juego el STATE es decir, un objeto que describe CÓMO está la aplicación en un momento determinado. Se puede modificar.
Para el uso de STATE, debemos volver al método habitual de creación de un component en React mediante CLASS ( en lugar de arrow function ):
Recuerda que para poder usar this
en el constructor, primero debes llamar a super()
** Investiga React Hooks, anda, que esto parece que ha cambiado un poquito
Recuerda una vez más "bindear" el this actual cuando estes pasando un método por PROPS a otro componente:
constructor() {
super();
this.state = {
robots: robots,
searchfields: '',
};
**this.onSearchChange = this.onSearchChange.bind(this)**
}
onSearchChange(event) {
const filteredRobots = this.state.robots.filter(robots => robots.name.toLowerCase().includes(this.state.searchfields.toLowerCase()))
console.log(filteredRobots)
}
OJO!!, tienes una forma mejor de bindear el this: crear la función mediante ARROW funcion (consulta tus notas: mediante una ARROW FUNCTION, el "this" se genera en el momento de creación, no en su invocación)
constructor() {
super();
this.state = {
robots: robots,
searchfields: '',
};
}
**onSearchChange =(event) =>** {
const filteredRobots = this.state.robots.filter(robots => robots.name.toLowerCase().includes(this.state.searchfields.toLowerCase()))
console.log(filteredRobots)
}
Actualmente tenemos el archivo que contiene los "robots" que se muestran en pantalla, dentro de la misma estructura de carpetas del proyecto. Vamos a llevarlo a un servidor externo (vease JSON placeholder)
Modificamos el STATE de nuestro App
component (SMART component, ojo) para que inicialmente tenga un array vacío de datos:
constructor() {
super();
this.state = {
robots: [],
searchfields: '',
};
}
Lanzaremos la petición al servidor donde se "almacenan" nuestros robots para traer esos datos una vez resuelta la misma.
Vamos a utilizar los "ciclos de vida" de REACT para cargar los datos nada más crear ("mounting") la aplicación.
Ten en cuenta el orden siguiente (constructor - render - componentDidMount - render):
¿Por qué?
Sencillo: el método componentDidMount
se ejecuta una vez que la aplicación se ha renderizado, PERO, el método render
se vuelve a lanzar (es del tipo Updating) cuando detecta cambios.
Nos ponemos manos a la obra para hacer el "fetch" y traernos los datos que queremos renderizar:
componentDidMount() {
fetch('https://jsonplaceholder.typicode.com/users')
.then(resp => resp.json())
.then(users => this.setState({robots: users}))
}
Vamos a crear un componente para poder hacer "scroll" en los ROBOTS sin perder el buscador superior
En este caso, vamos a usar la propiedad children
para manejar unas tags o componentes que están DENTRO de nuestro componente actual:
Para ello, dentro del componente Scroll, renderizaremos su contenido:
<Scroll>
<CardList robots="{filteredRobots}"></CardList>
</Scroll>
Le damos al componente scroll
una funcionalidad que nos permite "recoger" cualquier otro componente y añadir un scroll propio. Para esto hemos utilizado en este caso la opción de añadir estilos en línea:
const Scroll = (props) => {
return (
<div
style={{
overflowY: 'scroll',
border: '3px solid black',
height: '500px',
}}
>
{props.children}
</div>
);
};
El siguiente paso (que debería haber sido el primero...) es crear una estructura de carpetas adecuada para ayudar a cualquier persona que se adentre en el proyecto a saber dónde narices están las cosas.
Dentro de src
vamos a diferenciar dos subcarpetas principales:
components
- la utilizaremos para guardar componentes de renderizado o aquellos simples que sólo tienen PROPS (quizá lo podría explicar mejor)containers
- contiene los componentes SMART, aquellos que tienen un STATE interno y que se crean medianteclass Component extends React.Component{}
. Contienen "cosas" y pasan sus cambios a otros COMPONENTS
Tras aplicar los cambios básicos, la nueva estructura de carpetas queda de la siguiente forma:
Bueno, no está mal.
También aprovechamos para refactorizar el código en el componente App.js
render() {
const { robots, searchfield } = this.state;
const filteredRobots = robots.filter((robots) =>
robots.name.toLowerCase().includes(searchfield.toLowerCase())
);
return (!robots.length) ?
<h1>Loading ...</h1>
:
(
<div className="tc">
<h1 className="f1">RoboFriends</h1>
<SearchBox searchChange={this.onSearchChange}></SearchBox>
<Scroll>
<CardList robots={filteredRobots}></CardList>
</Scroll>
</div>
);
}
- Usamos "destructuring" para no tener que estar llamando continuamente a
this.state
- Cambiamos el IF-ELSE a una "ternary expression" (sólo si es más comprensible)
Es probable que a la hora de utilizar algún proyecto de nuestro repositorio nos encontremos con que las dependencias del package.json estén desactualizadas.
Incluso, en muchas ocasiones, nos encontramos con que al hacer el npm install
nos dice que se encontraron X vulnerabilidades:
Si ocurre esto podemos lanzar el comando npm audit fix
para solucionar aquellas vulnerabilidades que tengan una actualización "simple".
Se dan casos en los que nos dice que hay que hacerlo de forma manual (podemos usar el comando npm audit
para entrar en el detalle sin actualizar.
Si queremos hacerlo "a lo bestia", todavía podemos usar npm audit fix --force
. Pero si hacemos esto, hay que tener muy en cuenta el revisar la aplicación al detalle.
Ojo con actualizar al "tun tun" que podemos generar un cambio en algunas librerías que haga que la aplicación deje de funcionar
Para prevenir posibles errores a la hora de actualizar, las dependencias incorporan una especie de "control de versiones" que determinan hasta dónde se pueden actualizar estas. Un ejemplo:
"react": "^16.**10**.2"
Indica que sólo se puede realizar una actualización "minor", es decir, que podría cambiarse el segundo término de la misma.
Una vez terminada las actualizaciones de seguridad (comprobar en la pestaña de seguridad del proyecto), volvemos a hacer PR.
Como ejemplo práctico, ya que hemos empezado el proyecto con la versión 16 de React, vamos a hacer la actualización a la versión 17 que ya es estable:
npm install react@17.0.0 react-dom@17.0.0
Recuerda que DEBES revisar el proyecto después de una actualización importante.
Según la documentación de la v17 de React, no se incluyen cambios que puedan hacer dejar de funcionar la aplicación.
Vamos a ver cómo podemos realizar el "manejo de errores" en React, para que si, por lo que sea, uno de nuestros componentes deja de funcionar de la forma que se espera, no se produzca un fallo en toda la aplicación.
Creamos un componente ErrorBoundry
que encapsule los componentes que queremos "controlar" y se encargue de manejar sus errores:
- Si todo va bien, mostrar el componente
- Si algo falla, capturar el error gracias al componente de ciclo de vida
componentDidCatch(error, info) {}
y cambiar el estado interno delErrorBoundry
para que muestre el error
import React from 'react';
class ErrorBoundry extends React.Component {
constructor(props) {
super();
this.state = {
hasError: false,
}
}
// si ocurre un error, lo captura. Es como el "catch()"
**componentDidCatch(error, info) {
console.log(error, info);
this.setState({
hasError: true
})
}**
render() {
if (this.state.hasError) {
return <h1>Oops. That is not good</h1>
}
return this.props.children;
}
}
export default ErrorBoundry;
Si "wrapeamos" el CardList
component y generamos un error internamente en él:
const CardList = ({ robots }) => {
const cardComponent = robots.map((robot) => {
**if (true) {
throw new Error('NOOOOO!')
}**
return <Card
key={robot.id}
id={robot.id}
name={robot.name}
email={robot.email}
/>
})
return <div>
{cardComponent}
</div>;
}
El componente que hemos creado para manejar estos errores se activará y lanzará el mensaje que hemos configurado.
Pero ten en cuenta que en desarrollo, el manejo de errores lo hace React y nos mostrará la siguiente pantalla:
¡¡ Por lo tanto, esto que hemos hecho es útil para producción !!
Ya la tengo desplegada, para hacerlo seguimos los pasos que nos indica la propia página de React:
Atento porque también se indica como desplegar en otros sitios como FIREBASE, HEROKU ....
La idea de esta sección es hacer el cambio para usar "hooks" en los "class component" de la app robofriends
Los Hooks son una nueva característica de React que permite usar el estado y otras características de React sin necesidad de usar Clases. Ejemplo:
import React, { useState } from 'react';
function Example() {
// Declara una nueva variable de estado, la cual llamaremos “count”
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
Importante cuando hay una novedad en una librería: leer la documentación. Sobretodo la parte de "motivation" (el por qué de su inclusión)
Vamos a entender los que llevaron a crear los React Hooks:
- Es difícil reutilizar la lógica de estado entre componentes
- Los componentes complejos se vuelven difíciles de entender
- Las clases confunden tanto a las personas como a las máquinas (?)
Hasta la fecha, sólo usabamos "functional components" cuando creabamos componentes SIN estado, ahora la cosa va a cambiar.
class App extends Component {
constructor() {
super();
this.state = {
robots: [],
searchfield: '',
};
}
}
El esquema para convertir lo anterior en Hooks es la siguiente:
const [robots, setRobots] = useState([]);
const [searchfield, setSearchfield] = useState('');
- Usaremos "destructuring" para asignar las dos variables (un valor y una función)
- Como valor, recibirá el nombre del estado con el que trabajamos
- Como función, le asignaremos como norma
setLoquesea
- Igualamos al método de React
useState
(previamente importado), al que le pasamos como parámetro el estado inicial.
Ojo que ahora tampoco tenemos los tradicionales métodos del ciclo de vida de los componentes (estaban disponibles al heredar la clase React.Component
) por lo que lo eliminamos y usaremos, en el caso de nuestra aplicación, el Effect Hook
:
componentDidMount() {
fetch('https://jsonplaceholder.typicode.com/users')
.then(resp => resp.json())
.then(users => this.setState({robots: users}))
}
Si estás familiarizado con el ciclo de vida de las clases de React y sus métodos, el Hook useEffect equivale a componentDidMount, componentDidUpdate y componentWillUnmount combinados.
El cambo sería el siguiente:
useEffect(
() => {
fetch('https://jsonplaceholder.typicode.com/users')
.then((resp) => resp.json())
.then((users) => setRobots(users));
},
[
/*definimos aquí qué valores determinan cuándo debe lanzarse el useEffect */
]
);
Presta atención al parámetro adicional que puede recibir "useEffect" para no tener ciclos infinitos: "If present, affect will only activate if the values in the list change"
Sí, porque si añadimos un console.log
a nuestra función App
, veremos que se está ejecutando continuamente:
Si le pasamos, por ejemplo:
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/users')
.then((resp) => resp.json())
.then((users) => setRobots(users));
}, [searchfield]);
Sólo se ejecutará al cambiar searchfield
, no está mal, pero no es del todo óptimo.
Para tener un comportamiento igual al que teníamos antes con componentDidMount()
tenemos un pequeño "truqui" que es pasarle un ARRAY vacío:
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/users')
.then((resp) => resp.json())
.then((users) => setRobots(users));
}, []);
Le decimos que cambie cuando ... ¡no tiene condición!, así que no cambia 🤨. Sólo carga los datos 1 vez.
¿¿ Tengo que cambiar ahora todo el código ??
NO. Los hooks pueden convivir con normalidad con los class components.
De hecho en la misma página de React se desaconseja estar cambiando todo el proyecto existente, pero se recomienda empezar a usarlo en adelante.
Ten en cuenta estas dos recomendaciones:
Otra de las ideas es que podemos crear nuestros propios Hooks para ser compartidos y reutilizados posteriormente
En el siguiente apartado, queremos mejorar el "flujo" de la aplicación:
Cada uno de los componentes de nuestra aplicación puede tener un STATE propio, que determina la configuración del mismo y podríamos decir que es como la "memoria" la app:
Vale, pero ¿qué pasa cuando nuestra APP empieza a tener una cantidad muy considerable de componentes y estados diferentes?. Que gestionar estados "locales" empieza a ser algo bastante más complejo...
Imagina que los rojos son los que tienen STATE y los azules cambian el estado de otros
Y si pensáramos en un OBJETO masivo que contuviera todo el STATE de la aplicación y que cada uno de los componentes tuviera sólo (o no) PROPS ?
REDUX está para ayudarnos, toma ideas y conceptos de una estructura de BBDD
- Se lleva muy bien con React
- Es bueno para manejar "grandes estados"
- Muy útil para intercambiar datos entre contenedores (componentes)
- PREDICTABLE STATE MANAGEMENT USING THE 3 PRICIPLES:
- SINGLE SOURCE OF TRUTH: 1 sólo objeto que contiene todo el estado de la aplicación
- STATE IS READ ONLY: inmutabilidad, el objeto original no se modifica, se crea uno nuevo en cada cambio realizado (?)
- CHANGES USING PURE FUNCTIONS: como debería ser siempre!
REDUCER es un "filtro" que mediante funciones puras se encarga de manejar el STORE
Ten en cuenta el patrón que se utiliza en Redux:
Lo mismo te suena de NgRx en Angular, verdad?
Quedate con un concepto que simplifica las cosas:
En teoría, según esto, con REDUX no es necesario tener estados locales en los componentes de REACT.
Pero ojo, no es excluyente, podemos encontrar casos en los que se combinan ambos.
npm install redux
La configuración de REDUX se realiza en JS, y lo mejor de esto es que la librería maneja conceptos y muy buenas prácticas que mejorará nuestro conocimiento base en JS
Para conectar React con Redux, utilizamos la librería:
npm install react-redux
Salvo el componente SMART, el resto no tiene por qué tener constancia de la existencia de REACT. En nuestro proyecto (robofriends) se realizará la conexión con App.js
Existe una "cosita" llamada Redux Toolkit:
que se encarga de gestionar toda la configuración de REDUX en nuestra aplicación (ya que según se comenta es un poco compleja). Tenlo en cuenta, pero no lo vamos a usar en el curso ya que es mejor "mancharse las manos" haciéndolo todo de 0.
Según el esquema que vimos arriba, vamos a crear nuestras actions
, reducers
y constants
actions.js
import { CHANGE_SEARCHFIELD } from './constants';
export const setSearchField = (text) => ({
type: CHANGE_SEARCHFIELD,
payload: text,
});
reducers.js
import { CHANGE_SEARCHFIELD } from './constants';
const initialState = {
searchField: '',
};
export const searchRobots = (state = initialState, action = {}) => {
switch (action.type) {
case CHANGE_SEARCHFIELD:
return Object.assign({}, state, { searchField: action.payload });
default:
return state;
}
};
constants.js
export const CHANGE_SEARCHFIELD = 'CHANGE_SEARCHFIELD';
Una vez los hemos creado, vamos a conectarlos a nuestra aplicación React
Ahora podríamos, en teoría, pasarle el STORE como prop a App.js
¿Y voy a tener que estar pasando el STATE como PROP en cada componente?
NO. Para eso tenemos el conector <Provider></Provider>
y los conectores que veremos a continuación:
Las buenas prácticas dicen que a los componentes SMART los llamemos containers
y los agrupemos juntos, separados de los components
(no Smart)
Revisar la clase 105, en la que se explica al detalle un ejemplo de la integración de la aplicación "robofriends" con Redux. No estaría de más darle un par de vueltas antes de seguir, con el siguiente video:
https://www.youtube.com/watch?v=ngc9gnGgUdA&feature=youtu.be&ab_channel=JavaScriptMastery
Una vez terminada la integración del componente App.js
con Redux, queda de la siguiente forma:
import React, { useState, useEffect } from 'react';
import { connect } from 'react-redux';
import CardList from '../components/card-list/CardList';
import SearchBox from '../components/search-box/SearchBox';
import Scroll from '../components/scroll/Scroll'
import './App.css'
import ErrorBoundry from '../core/error/ErrorBoundry';
import { setSearchField } from '../redux/actions';
**const mapStateToProps = state => {
return {
searchField: state.searchField,
};
};
const mapDispatchToProps = (dispatch) => {
return {
onSearchChange: (event) =>{dispatch(setSearchField(event.target.value))}
}
}**
function App (props) {
const [robots, setRobots] = useState([]);
// const [searchfield, setSearchfield] = useState('');
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/users')
.then(resp => resp.json())
.then(users => setRobots( users))
},[]) // only run if [something] changes
// Recuerda que usamos un ARROW FUNCTION para que obtenga el THIS del "sitio" en el que se ha creado, NO de donde se invoca (en este caso en SearchBox Component)
// const onSearchChange = (event) => {
// setSearchfield( event.target.value)
// }
const filteredRobots = robots.filter((robots) =>
robots.name.toLowerCase().includes(props.searchField.toLowerCase())
);
return (!robots.length) ?
<h1>Loading ...</h1>
:
(
<div className="tc">
<h1 className="f1">RoboFriends</h1>
<SearchBox searchChange={props.onSearchChange}></SearchBox>
<Scroll>
<ErrorBoundry>
<CardList robots={filteredRobots}></CardList>
</ErrorBoundry>
</Scroll>
</div>
);
}
**export default connect(mapStateToProps, mapDispatchToProps)(App);**
Presta atención a las funciones mapStateToProps
y mapDispatchToProps
y a la forma de "exportar" el componente (connect(...)(App)
)
Para entenderlo mejor, podemos mostrar por consola lo que se "cuece" en nuestra única Action
:
import { CHANGE_SEARCH_FIELD } from './constants';
export const setSearchField = (text) => {
console.log('searchfield: ', text);
return { type: CHANGE_SEARCH_FIELD, payload: text };
};
Podemos introducir "middlewares" en la aplicación (entre las actions
y reducers
) para controlar, mapear y gestionar la ejecución de estos.
¿Para qué?
Un ejemplo... Empezamos instalando npm install redux-logger
Es una "librería" que nos permite monitorizar estados y acciones de la App a tiempo real (se ejecuta entre la acción y el reducer):
Vamos a cambiar ahora la petición asíncrona que hacemos para obtener los robots al iniciar el componente App.js
, para hacerla con la librería REDUX
Instalamos:
npm install redux-thunk
(Es otro Middleware)
Comenzamos creando tres nuevas acciones (es un estandar a la hora de trabajar con peticiones asíncronas):
export const REQUEST_ROBOTS_**PENDING** = 'REQUEST_ROBOTS_PENDING';
export const REQUEST_ROBOTS_**SUCCESS** = 'REQUEST_ROBOTS_SUCCESS';
export const REQUEST_ROBOTS_**FAIL** = 'REQUEST_ROBOTS_FAIL';
export const requestRobots = (dispatch) => {
dispatch({ type: REQUEST_ROBOTS_PENDING });
fetch('https://jsonplaceholder.typicode.com/users')
.then((resp) => resp.json())
.then((data) => dispatch({ type: REQUEST_ROBOTS_SUCCESS, payload: data }))
.catch((err) => dispatch({ type: REQUEST_ROBOTS_FAIL, payload: err }));
};
Una vez tenemos las acciones, creamos los reducers
:
const initialStateRobots = {
isPending: false,
robots: [],
err: '',
};
export const requestRobots = (state = initialStateRobots, action = {}) => {
switch (action.type) {
case REQUEST_ROBOTS_PENDING:
return Object.assign({}, state, { isPending: true });
case REQUEST_ROBOTS_SUCCESS:
return Object.assign({}, action, {
robots: action.payload,
isPending: false,
});
case REQUEST_ROBOTS_FAIL:
return Object.assign({}, state, {
err: action.payload,
isPending: false,
});
default:
return state;
}
};
Y ojo, porque ahora tenemos que hacer "consciente" al index.js
de que tenemos varios REDUCERS.
Utilizamos el método que viene con la librería de REACT: combineReducers
const rootReducer = combineReducers({ searchRobots, requestRobots });
const store = createStore(
rootReducer,
applyMiddleware(thunkMiddleware, logger)
);
Ojo porque ahora nuestra STORE contiene 2 estados, y cada vez que la llamemos, debemos indicarle en concreto cuál queremos utilizar. Veamos....
El nuevo "middleware" que estamos utilizando...
import thunkMiddleware from 'redux-thunk';
se ejecutará cada vez que uno de nuestros REDUCERS devuelva una función, no un objeto.
❗ Recuerda que para que no se produzca un bucle infinito de peticiones, en el método useEffect
hay que pasarle como segundo parámetro un ARRAY vacío
useEffect(() => props.onRequestRobots(), []);
Insisto una vez más, no está de más leer la documentación antes de empezar a usarlo:
¡¡ No es que no esté de más, es que es imperativo !!
Cuando el proyecto ya va teniendo cierta jerarquía, la cantidad de archivos JS que tenemos para los distintos componentes empieza a ser considerable, por lo que se hace muy necesario algún sistema para "empaquetar" y servir de manera más optimizada estos archivos.
El objetivo es "maximizar" la entrega efectiva de estos archivos.
Tenemos varias herramientas a día de hoy para hacer esto, aunque como sabemos, la más utilizada es WEBPACK:
¿Voy a usar el resto? Puede que sí, en las siguientes casuísticas:
- Si el proyecto es grande, WEBPACK, sin duda
- Si es para un pequeño proyecto personal, PARCEL
¡Vamos con Webpack!
LOADERS: herramientas que compilan o transpilan nuestro código, usadas por librerías como Webpack