cover

Streaming Server-Side Rendering o Renderizado del lado del servidor con Streams. Explicado

author photo

Héctorbliss

@hectorbliss


Mira el video si prefieres:

robot logo saying hello

No te quedes atrás: Actualízate

Suscríbete para recibir información sobre nuevos frameworks, updates, eventos, tips, hacks y más.

TLDR: Vamos a generar un HTML como primera respuesta al «request» del cliente, pero vamos a responder no con un archivo HTML, sino con pedacitos del mismo, con Streams.

En este post vamos a usar el término TTI (Time to interactive) que es una métrica de «progreso» que mide el performance del sitio web. En otras palabras: el tiempo que le tomará a un sitio web en entregar la data al cliente y que el cliente pueda interactuar con la página.

Y vamos a reducir al máximo ese TTI evitando la entrega de un archivo y en su lugar, justo como lo hacemos en este otro post con PDFs, pero en este caso con componentes de React. En el momento en que el cliente comience a recibir los pedacitos de datos, podrá, casi de inmediato, renderizar el contenido.

Vamos, pues, a explorar nuestra curiosidad, creando lo que se conoce como: STREAMING SERVER-SIDE RENDERING

¿Cuál de todas las herramientas de React server emplearemos?

Como te imaginarás, esto es posible gracias a los componentes de servidor de React. En el caso de los strems a dos herramientas específicas:

  • renderToPipeableStream
  • renderToNodeStream ~~ya está deprecado~~
  • renderToStaticNodeStream

En ese video, te muestro cómo usar el método renderToString para crear un SSR. Pero en esta ocasión no queremos trabajar con strings, sino con streams. Así que toca usar: renderToPipeableStream

Cómo se ve un SSR vs SSSR

SSR, server side rendering En un SSR típico, hay que generar todo el HTML y entregarlo completo, para luego ser leído y conseguir los enlaces y el bundle.

Veamos cómo sería con streams:

streaming server rendering Cómo puedes notar, el tiempo de respuesta del servidor se reduce al máximo, pues en vez de entregar todo un archivo, el servidor puede devolver el primer pedacito, con el que el cliente puede comenzar a renderizar. Para luego seguir recibiendo pedacitos que continúa renderizando así, como le van llegando.

Vamos al código

Esta es toda la app de express:

// index.js import React from "react"; import path from "path"; import express from "express"; import { renderToPipeableStream } from "react-dom/server"; import App from "../components/App.jsx"; const PORT = 3000; const app = express(); app.get("/favicon.ico", (req, res) => res.end()); app.use("client.js", (req, res) => res.redirect("/public/client.js")); app.use(express.static(path.resolve(__dirname, "public"))); app.use("/build", express.static(path.resolve(__dirname, "public"))); app.get("/", (req, res) => { const { pipe } = renderToPipeableStream( <div id="root"> <App /> </div>, { bootstrapScripts: ["/client.js"], onShellReady() { res.setHeader("content-type", "text/html"); res.write("<html><body><h1>Blissmo streaming</h1>"); pipe(res); }, } ); }); app.listen(PORT); console.log(`Running on: http://localhost:${PORT}`);

Como te puedes dar cuenta en el código; el componente <App/>es enviado como streams gracias al método renderToPipeableStream. El HTML inicial es enviado al objeto de respuesta junto con los pedacitos de datos del componente <App/>.

Todos estos datos contienen información útil que nuestra app debe usar para poder renderizar el contenido correctamente, hablamos de datos como el <title></title> y las hojas de estilo <link/>. Si usáramos el método renderToString tendríamos que esperar hasta que la aplicación reciba todos los datos antes de comenzar la carga, procesar los metadatos y posteriormente hidratar.

No olvidemos llamar a hydrateRoot en nuestro client.js

(Revisa el repo con todo el proyecto)

Con esto es posible que nuestra <App/> comience la carga y el procesamiento de información de inmediato, con la posibilidad de seguir recibiendo los pedacitos de datos del componente <App/> ¿fascinante no crees?

Esto tiene ventajas enormes, sobre todo hablando de optimización de memoria.

Además, el servidor utiliza mucha menos memoria y se mantiene mucho más responsivo, evitando casi por completo el bloqueo I/O por lo que es famoso Node.js y sus streams.

renderToPipeableStream está disponible desde la versión 16 de React (2016) pero hasta ahora está tomando mucha importancia, pues el desarrollo web está volviendo al servidor.

Otra ventaja que quiero mencionar aquí es que los streams pueden ser leídos por los crawlers. Esto significa que el SEO será interpretado perfectamente.

¿Qué tal?, ahora que entiendes cómo funcionan los Streams de React, ¿Te gustaría saber más sobre los experimentos que está haciendo el equipo de React con SSR?

Déjame saberlo en mi twitter.

Abrazo. Bliss.

Enlaces relacionados

El repo con todo el codigo funcionando

Entre PDFs como streams

Más sobre streams: Video

React components sin bundle.js

banner

¿Quieres mantenerte al día sobre los próximos cursos y eventos?

Suscríbete a nuestro newsletter

Jamás te enviaremos spam, nunca compartiremos tus datos y puedes cancelar tu suscripción en cualquier momento 😉

robot logo saying hello
facebook icontwitter iconlinkedin iconinstagram iconyoutube icon

© 2016 - 2023 Fixtergeek