Conocí la división de códigos hace mucho tiempo, en el año 2008, cuando Yandex estaba un poco suspendido, y los scripts Yandex.Direct conectados sincrónicamente al sitio simplemente mataron este sitio. En general, era normal en esos días si sus "secuencias de comandos" son 10 archivos que conecta en el único orden correcto, que todavía (con aplazamiento) todavía funciona bien.
Luego comencé a trabajar activamente con tarjetas, y todavía están conectadas como scripts externos, por supuesto, carga diferida. Luego, como miembro del equipo de Yandex.Maps, utilicé activamente ymodules para usar el intercambio de árboles en el cliente, lo que proporcionó una división de código perfecta.
Y luego me acerqué a webpack
y React
, al país de los idiotas asustados que miraban require.ensure
como un carnero en una nueva puerta, y aún lo hago.
La división de código no es una característica sorprendente, es imprescindible. Aún así, la SSR
no interferiría ...

Pequeña introducción
Hoy en día, cuando los paquetes se hacen más gordos todos los días, la división de códigos se vuelve más importante que nunca. Al principio, la gente salió de esta situación simplemente creando puntos de entrada separados para cada página de su aplicación, lo que generalmente es bueno, pero no funcionará para SPA.
Luego vino la función require.ensure
, hoy conocida como dynamic import
(solo importación), a través de la cual simplemente puede solicitar un módulo, que recibirá un poco más tarde.
La primera biblioteca sobre este caso para React fue cargable por reacción , el bombo en torno al cual todavía no me queda muy claro y que ya ha muerto (simplemente dejó de complacer al autor).
En este momento, la opción más o menos "oficial" será React.lazy
y React.lazy
-components (solo @loadable
), y la elección entre ellos es obvia:
- React.lazy es completamente incapaz de SSR (Representación del lado del servidor), de la palabra en general. Incluso en las pruebas, caerá sin bailes especiales con una pandereta, como "promesas sincrónicas".
- Una SSR cargable puede, y aunque admite Suspenso, no es peor que React.Lazy.
En particular, loadable admite envoltorios hermosos para cargar bibliotecas (loadable.lib, puede tomar moment.js en React renderProp), y ayuda al paquete web en el lado del servidor a recopilar una lista de scripts, estilos y recursos usados para la captación previa (que el paquete web en sí no sabe realmente). En general, lea la documentación oficial .
SSR
En general, todo el problema está en la RSS. Para CSR (Client Side Render), se ajustará React.lazy o un pequeño script con 10 líneas; esto definitivamente será suficiente, y no tiene sentido conectar una gran biblioteca externa. Pero en el servidor esto no será suficiente. Y si realmente no necesita un SSR, puede omitir más la lectura. No tienes problemas que necesites resolver por mucho tiempo.
La RSS es un dolor. Yo (de alguna manera) es uno de los mantenedores de componentes cargables y es horrible cuántos errores salen de diferentes lugares. Y con cada actualización, el paquete web vuela aún más.
SSR + CSS
Una fuente aún mayor de problemas con los SSR es CSS.
Si tiene componentes con estilo, no duele tanto, vienen con transform-stream
que agregará lo que se necesita al código final. Lo principal es que debe haber una versión de SC en todas partes, de lo contrario el enfoque no funcionará: una versión de SC no podrá contar nada más sobre sí misma, y a SC le encanta multiplicar (verifique su paquete). Para ser honesto, es precisamente debido a esta limitación que el enfoque generalmente falla .
La emoción C es más simple: su adaptador con styled
simplemente escupe <style>
delante del componente en sí, y el problema está resuelto. Simple, barato y alegre. En principio, es muy amigable para dispositivos móviles y optimiza enormemente la primera vista. Pero un poco estropea el segundo. Y personalmente, mi conciencia no me permite incorporar estilos como ese.
Con CSS ordinario (incluido el obtenido de varias bibliotecas CSS-in-JS con diferente magia) es aún más fácil: hay información sobre ellos en la columna del paquete web y se "sabe" qué CSS necesita estar conectado.
Orden de conexión
Aquí el perro se enterró a sí mismo. ¿Cuándo debo conectarme?
El significado de la división de código amigable de SSR es que antes de llamar a ReactDOM.hydrate
debe descargar todos los "componentes" que ya están presentes en la respuesta del servidor, pero los scripts actualmente cargados en el cliente no pueden permitirse.
Por lo tanto, todas las bibliotecas ofrecen una cierta devolución de llamada que se llamará cuando se necesite cargar todo y todo, y puede iniciar el cerebro . Este es el significado del trabajo de las bibliotecas de división de códigos SSR.
JS se puede cargar en cualquier momento y, por lo general, su lista se agrega al final del HTML, pero CSS, para que no haya FOUC, debe agregarse al principio.
Todas las bibliotecas pueden hacer esto para el renderToString
, y todas las bibliotecas no pueden hacer esto para renderToNodeStream
.
No importa si solo tiene JS (esto no sucede) o SC / Emotion (que se sumarán). Pero, si tienes "solo CSS", eso es todo. O estarán al final, o tendrán que usar renderToString
u otro almacenamiento en búfer, que proporcionará un retraso TTFB (Tiempo hasta el primer byte) y reducirá ligeramente la sensación de tener este SSR en general.
Y, por supuesto, todo esto está relacionado con el paquete web y de ninguna otra manera. Por lo tanto, con el debido respeto a Greg, el autor de los componentes cargables, propongo considerar otras opciones.
Lo siguiente es una agenda de tres partes, cuya idea principal es hacer algo que no se elimine y que no dependa del paquete.
1. Componente importado por reacción
React-Imported-Component no es un mal "cargador", con una interfaz más o menos estándar, muy similar a los componentes cargables, que pueden SSR para todo lo que se mueve.
La idea es muy simple.
No es necesario stats.json
, adaptarlo para la optimización del paquete web (concatenación o código común): solo necesita hacer coincidir la "etiqueta" de una importación en la clave de la matriz e importar de nuevo. Cómo se realizará como parte de un paquete específico, cuántos archivos se descargarán realmente y de dónde no es su problema.
Menos: el inicio de la carga de fragmentos "usados" se produce después de cargar el paquete principal, que almacena la asignación, que es un poco "posterior" que en el caso de los componentes cargables, que agregarán esta información directamente a HTML.
Sí, con CCS no funciona de la palabra de ninguna manera.
2. estilos usados
Pero los estilos usados solo funcionan con CSS, pero de la misma manera que los componentes react-importados.
- escanea todos los css (en el directorio de compilación)
- recuerda dónde se define qué clase
- analiza la salida renderToNodeStream (o la respuesta
renderToString
) - encuentra class = 'XXX', coincide con el archivo y lo escupe en la respuesta del servidor.
- (bueno, y luego teletransporta todos esos estilos a la cabeza para no romper el hidrato). Los componentes de estilo funcionan igual.
No hay demora TTBT, no está vinculado al paquete, un cuento de hadas. Funciona como un reloj si los estilos están bien escritos.
React-import-component + used-styles + parcel ejemplo de trabajo.
No es la ventaja más obvia: en el servidor, ambas bibliotecas harán "todo lo necesario" durante el inicio, hasta que el servidor express pueda recibir el primer cliente, y se sincronizarán completamente tanto en el servidor como durante las pruebas.
3. componente de reacción previa
Y la biblioteca cierra los tres primeros, lo que hace una "rehidratación parcial" , y lo hace de una manera tan abuela que me pregunto de inmediato. Ella realmente agrega "divas".
- en el servidor:
- envuelve un pedazo de madera en un div con una "identificación famosa"
- en el cliente:
- el constructor de componentes encuentra su propio div
- copia su innerHTML antes de que React lo tome.
- usa este HTML hasta que el cliente esté listo para
hydrate
- técnicamente, esto permite el uso de SSR híbrido (Rendertron)
const AsyncLoadedComponent = loadable(() => import('./deferredComponent')); const AsyncLoadedComponent = imported(() => import('./deferredComponent')); <PrerenderedComponent live={AsyncLoadedComponent.preload()} // when Promise got resolve - component will go "live" > <AsyncLoadedComponent /> // meanwhile you will see "preexisting" content </PrerenderedComponent>
Este enfoque no funciona con componentes cargables, ya que no regresa de una promesa de precarga . Esto es especialmente importante para bibliotecas como react-snap (y otros "prerrenders") que tienen "contenido" pero no han pasado por un SSR "real".

Desde el punto de vista del código, se trata de 10 líneas, más un poco más para obtener UID SSR-CSR estables teniendo en cuenta el orden aleatorio de carga y ejecución del código.
Bonificaciones:
- no tiene que esperar a que se "carguen todos los guiones" antes de comenzar los cerebros : los cerebros comenzarán cuando estén listos
- no tiene que cargar cerebros en absoluto, dejando datos SSR-ed (si no hay una versión SSR, los cerebros aún se cargarán). Como en los tiempos de jQuery.
- También puede implementar el almacenamiento en caché de flujos de bloques de renderizado grandes (teóricamente compatibles con Suspence), nuevamente utilizando el flujo de transformación.
- y serializar / deserializar el estado a / desde HTML, como durante jQuery
En principio, la serialización y la deserialización fueron la idea principal de crear una biblioteca para resolver el problema de duplicar el estado (imagen del artículo sobre SSR). El almacenamiento en caché llegó más tarde.

Total
En total, hay tres enfoques que pueden cambiar su visión de SSR y división de código. El primero funciona con la división de códigos JS, y no se rompe. El segundo funciona con la división de códigos CSS, y no se rompe. El tercero funciona a nivel HTML simplificando y acelerando algunos procesos, y nuevamente, no se rompe.
Enlaces a bibliotecas:
Artículos (en inglés)