Renderización del lado del servidor (SSR)
Note
SSR se refiere específicamente a los marcos front-end (por ejemplo, React, Preact, Vue y Svelte) que admiten ejecutar la misma aplicación en Node.js, prevenirlo a HTML, y finalmente hidratarla en el cliente. Si está buscando integración con los marcos tradicionales del lado del servidor, consulte la Guía de integración de backend en su lugar.
La siguiente guía también asume experiencia previa trabajando con SSR en su marco de elección, y solo se centrará en los detalles de integración específicos de VITE.
Low-level API
Esta es una API de bajo nivel destinada a autores de biblioteca y marco. Si su objetivo es crear una aplicación, asegúrese de consultar los complementos y herramientas SSR de nivel superior en la sección Awesome Vite SSR primero. Dicho esto, muchas aplicaciones se construyen con éxito directamente sobre la API nativa de bajo nivel de Vite.
Actualmente, Vite está trabajando en una API SSR mejorada con la API ambiental . Consulte el enlace para obtener más detalles.
Proyectos De Ejemplo
VITE proporciona soporte incorporado para la representación del lado del servidor (SSR). create-vite-extra
Contiene el ejemplo de configuraciones de SSR que puede usar como referencias para esta guía:
También puede andamiar estos proyectos localmente ejecutando create-vite
y elegir Others > create-vite-extra
en la opción Marco.
Estructura Fuente
Una aplicación SSR típica tendrá la siguiente estructura de archivo de origen:
- index.html
- server.js # main application server
- src/
- main.js # exports env-agnostic (universal) app code
- entry-client.js # mounts the app to a DOM element
- entry-server.js # renders the app using the framework's SSR API
El index.html
deberá referencia entry-client.js
e incluir un marcador de posición donde se debe inyectar el marcado renderizado del servidor:
<div id="app"><!--ssr-outlet--></div>
<script type="module" src="/src/entry-client.js"></script>
Puede usar cualquier marcador de posición que prefiera en lugar de <!--ssr-outlet-->
, siempre que pueda reemplazarse con precisión.
Lógica Condicional
Si necesita realizar una lógica condicional basada en SSR frente al cliente, puede usar
import 'vite/client'
// ---cortar---
if (import.meta.env.SSR) {
// ... Lógica de solo servidor
}
Esto se reemplaza estáticamente durante la construcción, por lo que permitirá la sacudida de las ramas no utilizadas.
Configuración Del Servidor Dev
Al construir una aplicación SSR, es probable que desee tener un control total sobre su servidor principal y Decouple Vite del entorno de producción. Por lo tanto, se recomienda usar VITE en modo middleware. Aquí hay un ejemplo con Express (V4):
import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import express from 'express'
import { createServer as createViteServer } from 'vite'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
async function createServer() {
const app = express()
// Create Vite server in middleware mode and configure the app type as
// 'custom', disabling Vite's own HTML serving logic so parent server
// can take control
const vite = await createViteServer({
server: { middlewareMode: true },
appType: 'custom'
})
// Use vite's connect instance as middleware. If you use your own
// express router (express.Router()), you should use router.use
// When the server restarts (for example after the user modifies
// vite.config.js), `vite.middlewares` is still going to be the same
// reference (with a new internal stack of Vite and plugin-injected
// middlewares). The following is valid even after restarts.
app.use(vite.middlewares)
app.use('*', async (req, res) => {
// serve index.html - we will tackle this next
})
app.listen(5173)
}
createServer()
Aquí vite
es una instancia de Vitedevserver . vite.middlewares
es una instancia de conexión que se puede usar como un middleware en cualquier marco Node.js compatible con Connect Compatible.
El siguiente paso es implementar el controlador *
para servir HTML renderizado por servidor:
import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
/** @Type {import ('express'). Express} */
var app
/** @Type {import ('vite'). Vitedevserver} */
var vite
// ---cortar---
app.use('*', async (req, res, next) => {
const url = req.originalUrl
try {
// 1. Leer index.html
let template = fs.readFileSync(
path.resolve(__dirname, 'index.html'),
'utf-8',
)
// 2. Aplique transformaciones VITE HTML. Esto inyecta el cliente VITE HMR,
// y también aplica las transformaciones HTML de los complementos VITE, p. global
// Preamplicles de @vitejs/plugin-react
template = await vite.transformIndexHtml(url, template)
// 3. Cargue la entrada del servidor. SSRLoadModule se transforma automáticamente
// El código fuente de ESM se puede utilizar en Node.js! No hay agrupación
// requerido y proporciona una invalidación eficiente similar a la HMR.
const { render } = await vite.ssrLoadModule('/src/entry-server.js')
// 4. Renderiza la aplicación html. Esto supone que la entrada-server.js exportó
// `render` la función llama a las API de SSR de marco apropiado,
// p.ej. Reactomserver.renderToString ()
const appHtml = await render(url)
// 5. Inyecte el HTML renderizado con aplicaciones en la plantilla.
const html = template.replace(`<!--ssr-outlet-->`, () => appHtml)
// 6. Envíe el HTML renderizado.
res.status(200).set({ 'Content-Type': 'text/html' }).end(html)
} catch (e) {
// Si se atrapa un error, deje que Vite arregle el rastro de la pila para que se mapea hacia atrás
// a su código fuente real.
vite.ssrFixStacktrace(e)
next(e)
}
})
El script dev
en package.json
también debe cambiarse para usar el script del servidor en su lugar:
"scripts": {
- "dev": "vite"
+ "dev": "node server"
}
Edificio para la producción
Para enviar un proyecto SSR para la producción, necesitamos:
- Producir una construcción de un cliente como de costumbre;
- Producir una compilación SSR, que se puede cargar directamente a través de
import()
para que no tengamos que pasar porssrLoadModule
de Vite;
Nuestros scripts en package.json
se verán así:
{
"scripts": {
"dev": "node server",
"build:client": "vite build --outDir dist/client",
"build:server": "vite build --outDir dist/server --ssr src/entry-server.js"
}
}
Tenga en cuenta el indicador --ssr
que indica que esta es una compilación SSR. También debe especificar la entrada SSR.
Luego, en server.js
necesitamos agregar algo de lógica específica de producción verificando process.env.NODE_ENV
:
En lugar de leer la raíz
index.html
, use eldist/client/index.html
como plantilla, ya que contiene los enlaces de activos correctos a la compilación del cliente.En lugar de
await vite.ssrLoadModule('/src/entry-server.js')
, useimport('./dist/server/entry-server.js')
(este archivo es el resultado de la compilación SSR).Mueva la creación y todo el uso del servidor
vite
DEV detrás de las ramas condicionales de desarrollo, luego agregue un archivo estático que sirva a los middlewares para servir archivos dedist/client
.
Consulte los proyectos de ejemplo para una configuración de trabajo.
Generar Directivas De Precarga
vite build
admite el indicador --ssrManifest
que generará .vite/ssr-manifest.json
en el directorio de salida de compilación:
- "build:client": "vite build --outDir dist/client",
+ "build:client": "vite build --outDir dist/client --ssrManifest",
El script anterior ahora generará dist/client/.vite/ssr-manifest.json
para la compilación del cliente (sí, el manifiesto SSR se genera a partir de la compilación del cliente porque queremos asignar ID de módulo a archivos del cliente). El manifiesto contiene asignaciones de ID de módulo a sus fragmentos asociados y archivos de activos.
Para aprovechar el manifiesto, los marcos deben proporcionar una forma de recopilar las ID de módulo de los componentes que se usaron durante una llamada de renderizado del servidor.
@vitejs/plugin-vue
admite esto fuera de la caja y registra automáticamente las ID de módulo de componentes en el contexto de SSR VUE asociado:
const ctx = {}
const html = await vueServerRenderer.renderToString(app, ctx)
// ctx.modules es ahora un conjunto de ID de módulo que se usaron durante el renderizado
En la rama de producción de server.js
necesitamos leer y pasar el manifiesto a la función render
exportada por src/entry-server.js
. ¡Esto nos proporcionaría suficiente información para representar las directivas de precarga para los archivos utilizados por las rutas Async! Ver fuente de demostración para un ejemplo completo. También puede usar esta información para 103 pistas tempranas .
Preengivencia / SSG
Si las rutas y los datos necesarios para ciertas rutas se conocen con anticipación, podemos prever estas rutas en HTML estático utilizando la misma lógica que la SSR de producción. Esto también puede considerarse una forma de generación de sitios estáticos (SSG). Consulte el script previo al renderizado de demostración para el ejemplo de trabajo.
SSR externos
Las dependencias se "externalizan" del sistema de módulo de transformación SSR de Vite de forma predeterminada cuando se ejecuta SSR. Esto acelera tanto el desarrollo como la construcción.
Si una dependencia necesita ser transformada por la tubería de Vite, por ejemplo, porque las características de VITE se usan sin transferencia en ellas, se pueden agregar a ssr.noExternal
.
Para las dependencias vinculadas, no se externalizan de forma predeterminada para aprovechar el HMR de Vite. Si no se desea, por ejemplo, para probar las dependencias como si no estuvieran vinculados, puede agregarlo a ssr.external
.
Working with Aliases
Si ha configurado alias que redirigen un paquete a otro, es posible que desee alias los node_modules
paquetes reales para que funcione para dependencias externalizadas de SSR. Tanto el hilo como el PNPM admiten aliasing a través del prefijo npm:
.
Lógica De Complemento Específico De SSR
Algunos marcos como Vue o Svelte compilan componentes en diferentes formatos basados en el cliente frente a SSR. Para admitir transformaciones condicionales, Vite pasa una propiedad ssr
adicional en el objeto options
de los siguientes ganchos de complemento:
resolveId
load
transform
Ejemplo:
/** @Type {() => import ('vite'). Plugin} */
// ---cortar---
export function mySSRPlugin() {
return {
name: 'my-ssr',
transform(code, id, options) {
if (options?.ssr) {
// Realizar transformación específica de SSR ...
}
},
}
}
El objeto de opciones en load
y transform
es opcional, el rollup no está utilizando este objeto, pero puede extender estos ganchos con metadatos adicionales en el futuro.
Note
Antes de Vite 2.7, esto se informó a los ganchos de complemento con un parámetro ssr
posicional en lugar de usar el objeto options
. Todos los principales marcos y complementos se actualizan, pero puede encontrar publicaciones obsoletas utilizando la API anterior.
Objetivo de SSR
El objetivo predeterminado para la compilación SSR es un entorno de nodo, pero también puede ejecutar el servidor en un trabajador web. La resolución de entrada de paquetes es diferente para cada plataforma. Puede configurar el objetivo para ser trabajador web utilizando el ssr.target
establecido en 'webworker'
.
Paquete de SSR
En algunos casos como webworker
RunTimes, es posible que desee agrupar su construcción de SSR en un solo archivo JavaScript. Puede habilitar este comportamiento estableciendo ssr.noExternal
a true
. Esto hará dos cosas:
- Tratar todas las dependencias como
noExternal
- Tire un error si se importan nodo.js empotrados
Condiciones de resolución de SSR
Por defecto, la resolución de entrada del paquete utilizará las condiciones establecidas en resolve.conditions
para la compilación SSR. Puede usar ssr.resolve.conditions
y ssr.resolve.externalConditions
para personalizar este comportamiento.
VITE CLI
Los comandos CLI $ vite dev
y $ vite preview
también se pueden usar para aplicaciones SSR. Puede agregar su SSR MiddleWares al servidor de desarrollo con configureServer
y al servidor de vista previa con configurePreviewServer
.
Note
Use un gancho de publicación para que su middleware SSR se ejecute después de los WidleSwares de Vite.