El 2021 JavaScript Full-Stack Bootcamp está ABIERTO PARA INSCRIPCIONES!
Server Side Rendering, también llamado SSR, es la capacidad de una aplicación JavaScript para renderizar en el servidor en lugar de en el navegador.
¿Por qué querríamos hacerlo?
- Permite que tu sitio tenga un tiempo de carga de la primera página más rápido, que es la clave para una buena experiencia de usuario
- Es esencial para el SEO: los motores de búsqueda no pueden (¿todavía?) indexar de forma eficiente y correcta las aplicaciones que se renderizan exclusivamente del lado del cliente. A pesar de las últimas mejoras en la indexación de Google, hay otros motores de búsqueda también, y Google no es perfecto en cualquier caso. Además, Google favorece a los sitios con tiempos de carga rápidos, y tener que cargar del lado del cliente no es bueno para la velocidad
- es genial cuando la gente comparte una página de tu sitio en las redes sociales, ya que pueden reunir fácilmente los metadatos necesarios para compartir amablemente el enlace (imágenes, título, descripción..)
Sin Server Side Rendering, todo lo que su servidor envía es una página HTML sin cuerpo, sólo algunas etiquetas de script que luego son utilizadas por el navegador para renderizar la aplicación.
Las aplicaciones renderizadas por el cliente son grandes en cualquier interacción posterior del usuario después de la primera carga de la página. El renderizado del lado del servidor nos permite obtener el punto dulce en el medio de las aplicaciones renderizadas por el cliente y las aplicaciones renderizadas por el backend: la página se genera del lado del servidor, pero todas las interacciones con la página una vez cargada se manejan del lado del cliente.
Sin embargo, el renderizado del lado del servidor también tiene sus inconvenientes:
- es justo decir que una simple prueba de concepto de SSR es simple, pero la complejidad de SSR puede crecer con la complejidad de su aplicación
- renderizar una gran aplicación del lado del servidor puede ser bastante intensivo en recursos, y bajo una carga pesada podría incluso proporcionar una experiencia más lenta que el renderizado del lado del cliente, ya que tienes un único cuello de botella
Un ejemplo muy simplista de lo que supone renderizar del lado del servidor una aplicación React
Las configuraciones de React pueden llegar a ser muy, muy complejas y la mayoría de los tutoriales incorporan Redux, React Router y muchos otros conceptos desde el principio.
Para entender cómo funciona SSR, vamos a empezar por lo básico para implementar una prueba de concepto.
Siéntete libre de saltarte este párrafo si sólo quieres mirar las librerías que proporcionan SSR y no molestarte con el trabajo de base
Para implementar SSR básico vamos a usar Express.
Si eres nuevo en Express, o necesitas ponerte al día, echa un vistazo a mi manual gratuito de Express aquí: https://flaviocopes.com/page/ebooks/.
Advertencia: la complejidad de SSR puede crecer con la complejidad de su aplicación. Esta es la configuración mínima para renderizar una aplicación React básica. Para necesidades más complejas es posible que tenga que hacer un poco más de trabajo o también comprobar las bibliotecas SSR para React.
Supongo que usted comenzó una aplicación React con create-react-app
. Si sólo estás probando, instala una ahora usando npx create-react-app ssr
.
Ve a la carpeta principal de la app con el terminal, luego ejecuta:
npm install express
Tienes un conjunto de carpetas en tu directorio de la app. Crea una nueva carpeta llamada server
, luego entra en ella y crea un archivo llamado server.js
.
Siguiendo las convenciones de create-react-app
, la app vive en el archivo src/App.js
. Vamos a cargar ese componente, y a renderizarlo a una cadena usando ReactDOMServer.renderToString(), que es proporcionada por react-dom
.
Obtenemos el contenido del archivo ./build/index.html
, y reemplazamos el marcador de posición <div></div>
, que es la etiqueta donde se engancha la aplicación por defecto, con `<div>${ReactDOMServer.renderToString(<App />)}</div>
.
Todo el contenido dentro de la carpeta build
va a ser servido tal cual, estáticamente por Express.
import path from 'path'import fs from 'fs'import express from 'express'import React from 'react'import ReactDOMServer from 'react-dom/server'import App from '../src/App'const PORT = 8080const app = express()const router = express.Router()const serverRenderer = (req, res, next) => { fs.readFile(path.resolve('./build/index.html'), 'utf8', (err, data) => { if (err) { console.error(err) return res.status(500).send('An error occurred') } return res.send( data.replace( '<div></div>', `<div>${ReactDOMServer.renderToString(<App />)}</div>` ) ) })}router.use('^/$', serverRenderer)router.use( express.static(path.resolve(__dirname, '..', 'build'), { maxAge: '30d' }))// tell the app to use the above rulesapp.use(router)// app.use(express.static('./build'))app.listen(PORT, () => { console.log(`SSR running on port ${PORT}`)})
Ahora, en la aplicación cliente, en su src/index.js
, en lugar de llamar a ReactDOM.render()
:
ReactDOM.render(<App />, document.getElementById('root'))
llama a ReactDOM.hydrate()
, que es lo mismo pero tiene la capacidad adicional de adjuntar escuchadores de eventos al marcado existente una vez que React carga:
ReactDOM.hydrate(<App />, document.getElementById('root'))
Todo el código de Node.js necesita ser transpilado por Babel, ya que el código Node.js del lado del servidor no sabe nada de JSX, ni de ES Modules (que usamos para las declaraciones ).
Instala estos 4 paquetes:
npm install @babel/register @babel/preset-env @babel/preset-react ignore-styles
ignore-styles
es una utilidad de Babel que le dirá que ignore los archivos CSS importados usando la sintaxis import
.
Creemos un punto de entrada en server/index.js
:
require('ignore-styles')require('@babel/register')({ ignore: , presets: })require('./server')
Construye la aplicación React, para que se rellene la carpeta build/:
npm run build
y ejecutemos esto:
node server/index.js
He dicho que es un enfoque simplista, y lo es:
- no maneja el renderizado de imágenes correctamente cuando se usan importaciones, que necesitan de Webpack para funcionar (y que complica mucho el proceso)
- no maneja los metadatos de la cabecera de la página, que son esenciales para propósitos de SEO y de compartir en redes sociales (entre otras cosas)
Así que aunque este es un buen ejemplo de uso de ReactDOMServer.renderToString()
y ReactDOM.hydrate
para conseguir este renderizado básico del lado del servidor, no es suficiente para el uso en el mundo real.
El renderizado del lado del servidor usando librerías
SSR es difícil de hacer bien, y React no tiene una forma de facto de implementarlo.
Sigue siendo muy discutible si vale la pena el problema, la complicación y la sobrecarga para obtener los beneficios, en lugar de usar una tecnología diferente para servir esas páginas. Esta discusión en Reddit tiene muchas opiniones al respecto.
Cuando el renderizado del lado del servidor es un asunto importante, mi sugerencia es confiar en librerías y herramientas prefabricadas que han tenido este objetivo en mente desde el principio.
En particular, sugiero Next.js y Gatsby.
Descarga mi manual gratuito de React
¡El 2021 JavaScript Full-Stack Bootcamp YA ESTÁ ABIERTO PARA INSCRIPCIONES HASTA EL PRÓXIMO MARTES! No pierdas esta oportunidad, ¡inscríbete HOY!
Más tutoriales de React:
- Configuración de VS Code para el desarrollo de React
- Concepto de React: Inmutabilidad
- Cómo devolver múltiples elementos en JSX
- Portales React
- Cómo renderizar HTML en React
- La hoja de ruta para aprender React
- ¿Deberías usar jQuery o React?
- Cómo usar el hook useRef de React
- React Router, cómo obtener datos de una ruta dinámica