Por Rohit Kumar
Aquí está lo que construiremos en este tutorial: una bonita tarjeta React como esta.
En este tutorial, usaremos el renderizado del lado del servidor para entregar una respuesta HTML cuando un usuario o un crawler golpea la URL de una página. Manejaremos estas últimas peticiones en el lado del cliente.
¿Por qué lo necesitamos?
Déjame guiarte hacia la respuesta.
- ¿Cuál es la diferencia entre el renderizado del lado del cliente y el renderizado del lado del servidor?
- Cons de renderizar React en el servidor
- ¿Cuándo se debe utilizar el Server Side Rendering?
- SEO
- Mejorar el rendimiento
- Reacciona renderizado en el servidor
- Reacciona renderizado en el navegador del cliente
- ¿Cómo funciona? – (4 sencillos pasos)
- Comenzar configurando nuestra App
- ¿Por qué necesitamos compilar los archivos fuente?
- Copia los archivos estáticos &precodificados
- Lado Servidor
- src/server.js
- 2. src/template.js
- El lado cliente
- src/bundle.js
- src/client.js
- Poniéndolo todo junto
- Index.js
- Construir & Ejecutar
- ¿Listo para convertirse en un React Pro?
- Gracias por leer esto.
¿Cuál es la diferencia entre el renderizado del lado del cliente y el renderizado del lado del servidor?
En el renderizado del lado del cliente, tu navegador descarga una página HTML mínima. Renderiza el JavaScript y rellena el contenido en ella.
El renderizado del lado del servidor, por otro lado, renderiza los componentes de React en el servidor. La salida es el contenido HTML.
Puedes combinar estos dos para crear una app isomórfica.
Cons de renderizar React en el servidor
- SSR puede mejorar el rendimiento si tu aplicación es pequeña. Pero también puede degradar el rendimiento si es pesada.
- Aumenta el tiempo de respuesta (y puede ser peor si el servidor está ocupado).
- Aumenta el tamaño de la respuesta, lo que significa que la página tarda más en cargar.
- Aumenta la complejidad de la aplicación.
¿Cuándo se debe utilizar el Server Side Rendering?
A pesar de estas consecuencias del SSR, hay algunas situaciones en las que se puede y se debe utilizar.
SEO
Todo sitio web quiere aparecer en las búsquedas. Corrígeme si me equivoco.
Desgraciadamente, los rastreadores de los motores de búsqueda todavía no entienden/renden JavaScript.
Esto significa que ven una página en blanco, sin importar lo útil que sea tu sitio.
Mucha gente dice que el rastreador de Google ahora rinde JavaScript.
Para probar esto, desplegué la aplicación en Heroku. Esto es lo que vi en la Consola de Búsqueda de Google:
Una página en blanco.
Esta fue la mayor razón por la que exploré el renderizado del lado del servidor. Especialmente cuando se trata de una página de piedra angular como una página de destino, blog, y así sucesivamente.
Para verificar si Google renderiza su sitio, visite:
Search Console Dashboard > Crawl > Fetch as Google. Introduzca la URL de la página o déjela vacía para la página principal.
Seleccione FETCH AND RENDER. Una vez completado, haga clic para ver el resultado.
Mejorar el rendimiento
En SSR, el rendimiento de la aplicación depende de los recursos del servidor y de la velocidad de la red del usuario. Esto lo hace muy útil para los sitios con mucho contenido.
Por ejemplo, digamos que tienes un teléfono móvil de precio medio con una velocidad de Internet lenta. Intenta acceder a un sitio que descarga 4 MB de datos antes de poder ver nada.
¿Podría ver algo en su pantalla en 2-4 segundos?
¿Volvería a visitar ese sitio?
No creo que lo haga.
Otra mejora importante es el tiempo de primera interacción del usuario. Esta es la diferencia en el tiempo desde que un usuario pulsa la URL hasta que ve el contenido.
Aquí está la comparación. Lo he probado en un Mac de desarrollo.
Reacciona renderizado en el servidor
El tiempo de primera interacción es de 300ms. La hidratación termina a los 400ms. El evento de carga sale a los 500ms aproximadamente. Puedes ver esto revisando la imagen de arriba.
El primer tiempo de interacción es de 400ms. El evento de carga sale a 470ms.
El resultado habla por sí mismo. Hay una diferencia de 100ms en el Tiempo de Primera Interacción con el Usuario para una app tan pequeña.
¿Cómo funciona? – (4 sencillos pasos)
- Crear un Redux Store fresco en cada petición.
- Despachar opcionalmente algunas acciones.
- Sacar el estado del Store y realizar SSR.
- Enviar el estado obtenido en el paso anterior junto con la respuesta.
Utilizaremos el estado pasado en la respuesta para crear el estado inicial en el lado del cliente.
Antes de empezar, clona/descarga el ejemplo completo de Github y utilízalo como referencia.
Comenzar configurando nuestra App
Primero, abre tu editor y shell favorito. Crear una nueva carpeta para su aplicación. Vamos a empezar.
npm init --yes
Rellena los detalles. Después de crear package.json
, copia las dependencias y scripts de abajo en ella.
Instala todas las dependencias ejecutando:
npm install
Necesitas configurar Babel y webpack para que nuestro script de construcción funcione.
Babel transforma ESM y react en código Node y entendido por el navegador.
Crea un nuevo archivo .babelrc
y pon la línea de abajo en él.
{ "presets": }
webpack agrupa nuestra app y sus dependencias en un solo archivo. Crea otro archivo webpack.config.js
con el siguiente código:
const path = require('path');module.exports = { entry: { client: './src/client.js', bundle: './src/bundle.js' }, output: { path: path.resolve(__dirname, 'assets'), filename: ".js" }, module: { rules: }}
El proceso de construcción produce dos archivos:
-
assets/bundle.js
– aplicación pura del lado del cliente. -
assets/client.js
– compañero del lado del cliente para SSR.
La carpeta src/
contiene el código fuente. Los archivos compilados de Babel van en views/
. El directorio views
se creará automáticamente si no está presente.
¿Por qué necesitamos compilar los archivos fuente?
La razón es la diferencia de sintaxis entre ESM & CommonJS. Mientras escribimos React y Redux, usamos mucho import y export en todos los archivos.
Desgraciadamente, no funcionan en Node. Aquí viene Babel al rescate. El script de abajo le dice a Babel que compile todos los archivos en el directorio src
y ponga el resultado en views.
"babel": "babel src -d views",
Ahora, Node puede ejecutarlos.
Copia los archivos estáticos &precodificados
Si ya has clonado el repositorio, copia desde él. Si no, descarga el archivo ssr-static.zip de Dropbox. Extráelo y guarda estas tres carpetas dentro del directorio de tu app. Esto es lo que contienen.
- React
App
y componentes reside ensrc/components
. - Archivos Redux en
src/redux/
. -
assets/ & media/
: Contiene archivos estáticos comostyle.css
e imágenes.
Lado Servidor
Crea dos nuevos archivos llamados server.js
y template.js
dentro de la carpeta src/
.
src/server.js
La magia ocurre aquí. Este es el código que has estado buscando.
import React from 'react';import { renderToString } from 'react-dom/server';import { Provider } from 'react-redux';import configureStore from './redux/configureStore';import App from './components/app';module.exports = function render(initialState) { // Model the initial state const store = configureStore(initialState); let content = renderToString(<Provider store={store} ><App /></Provider>); const preloadedState = store.getState(); return { content, preloadedState };};
En lugar de renderizar nuestra aplicación, necesitamos envolverla en una función y exportarla. La función acepta el estado inicial de la aplicación.
Así es como funciona.
- Pasa
initialState
aconfigureStore()
.configureStore()
Devuelve una nueva instancia de Store. Mantenla dentro de la variablestore
. - Llama al método
renderToString()
, proporcionando nuestra App como entrada. Renderiza nuestra app en el servidor y devuelve el HTML producido. Ahora, la variablecontent
almacena el HTML. - Obtén el estado de Redux Store llamando a
getState()
enstore
. Guárdalo en una variablepreloadedState
. - Devuelve el
content
ypreloadedState
. Los pasaremos a nuestra plantilla para obtener la página HTML final.
2. src/template.js
template.js
Exporta una función. Toma title
, state
y content
como entrada. Los inyecta en la plantilla y devuelve el documento HTML final.
Para pasar el estado, la plantilla adjunta state
a window.__STATE__
dentro de una etiqueta <scri
pt>.
Ahora se puede leer state
en el lado del cliente accediendo a window.__STATE__
.
También incluimos la aplicación del lado del cliente assets/client.js
del compañero SSR en otra etiqueta script.
Si solicitas la versión cliente pura, sólo pone assets/bundle.js
dentro de la etiqueta script.
El lado cliente
El lado cliente es bastante sencillo.
src/bundle.js
Así es como se escribe la envoltura de React y Redux Provider
. Es nuestra aplicación pura del lado del cliente. No hay trucos aquí.
import React from 'react';import { render } from 'react-dom';import { Provider } from 'react-redux';import configureStore from './redux/configureStore';import App from './components/app';const store = configureStore();render( <Provider store={store} > <App /> </Provider>, document.querySelector('#app'));
src/client.js
¿Te resulta familiar? Sí, no hay nada especial excepto window.__STATE__.
Todo lo que tenemos que hacer es coger el estado inicial de window.__STATE__
y pasarlo a nuestra función configureStore()
como estado inicial.
Echemos un vistazo a nuestro nuevo archivo de cliente:
import React from 'react';import { hydrate } from 'react-dom';import { Provider } from 'react-redux';import configureStore from './redux/configureStore';import App from './components/app';const state = window.__STATE__;delete window.__STATE__;const store = configureStore(state);hydrate( <Provider store={store} > <App /> </Provider>, document.querySelector('#app'));
Revisemos los cambios:
- Reemplaza
render()
porhydrate()
.hydrate()
es lo mismo querender()
pero se utiliza para hidratar elementos renderizados porReactDOMServer
. Asegura que el contenido es el mismo en el servidor y en el cliente. - Lee el estado del objeto ventana global
window.__STATE__
. Almacénalo en una variable y borra elwindow.__STATE__
. - Crea un almacén fresco con
state
como initialState.
Todo hecho aquí.
Poniéndolo todo junto
Index.js
Este es el punto de entrada de nuestra aplicación. Se encarga de las peticiones y de las plantillas.
También declara una variable initialState
. La he modelado con datos en el archivo assets/data.json
. La pasaremos a nuestra función ssr()
.
Nota: Al referenciar un fichero que está dentro de src/
desde un fichero que está fuera de src/
, utiliza el require()
normal y sustituye src/
por views/
. Ya sabes la razón (compilación de Babel).
Rutado
-
/
: Por defecto página de inicio renderizada por el servidor. -
/client
: Ejemplo de renderizado puro del lado del cliente. -
/exit
: Botón de parada del servidor. Sólo disponible en desarrollo.
Construir & Ejecutar
Es hora de construir y ejecutar nuestra aplicación. Podemos hacer esto con una sola línea de código.
npm run build && npm run start
Ahora, la aplicación se está ejecutando en http://localhost:3000.
¿Listo para convertirse en un React Pro?
Empiezo una nueva serie a partir del próximo lunes para obtener sus habilidades React ardiente, inmediatamente.
Gracias por leer esto.
Si te gusta y te resulta útil, sígueme en Twitter &Webflow.