Renderización del lado del servidor (SSR)

Al utilizar Element Plus para el desarrollo de SSR, es necesario llevar a cabo una manipulación especial durante SSR para evitar errores de hidratación.

TIP

Para los usuarios de Nuxt, proporcionamos un módulo Nuxt que contiene estos procesos especiales. Basta con instalarlo.

Proporcionar un ID

El valor proporcionado se utiliza para generar el ID único en Element Plus. Debido a que los diferentes IDs son propensos a hidratar errores en SSR, para asegurar que la parte del servidor y la parte del cliente generen el mismo ID, necesitamos inyectar el ID_injection_key en Vue.

// src/main.js (irrelevant code omitted)
import { createApp } from 'vue'
import { ID_INJECTION_KEY } from 'element-plus'
import App from './App.vue'

const app = createApp(App)
app.provide(ID_INJECTION_KEY, {
  prefix: 1024,
  current: 0,
})

Provide ZIndex

When you using SSR for development, you may encounter hydration errors caused by z-index. In this case, we recommend injecting an initial value to avoid such errors.

// src/main.js (irrelevant code omitted)
import { createApp } from 'vue'
import { ZINDEX_INJECTION_KEY } from 'element-plus'
import App from './App.vue'

const app = createApp(App)
app.provide(ZINDEX_INJECTION_KEY, { current: 0 })

Teleports

Teleport es utilizado internamente por varios componentes en Element Plus (por ejemplo. ElDialog, ElDrawer, ElTooltip, ElDropdown, ElSelect, ElDatePicker ...), por lo que es necesario un manejo especial durante SSR.

Procesa el teletransporte en el montaje (onMounted)

Una solución más fácil es hacer el Teleport condicionalmente en el montaje.

Por ejemplo, utilice el componente ClientOnly en Nuxt.

<client-only>
  <el-tooltip content="the tooltip content">
    <el-button>tooltip</el-button>
  </el-tooltip>
</client-only>

o

<script setup>
import { ref } from 'vue'

const isClient = ref(false)

onMounted(() => {
  isClient.value = true
})
</script>

<template>
  <el-tooltip v-if="isClient" content="the tooltip content">
    <el-button>tooltip</el-button>
  </el-tooltip>
</template>

Inyectar el marcado de teletransporte

Otra forma es inyectar el marcado de teletransporte en la ubicación correcta del HTML al final de la página.

WARNING

Puede haber algunos problemas de SSR con el teletransporte, así que debes prestar atención a las siguientes precauciones.

  1. El atributo teletransportado en todos los componentes basados en ElTooltip debe ser consistente, se recomienda utilizar el valor predeterminado.
  2. El valor del atributo append-to-body de ElDialog y ElDrawer debe ser consistente, se recomienda habilitar el append-to-body.
  3. When the ElSubMenu component has a multi-layer popup, It is recommended to enable the teleported

Necesitas inyectar el marcado de teletransporte cerca de la etiqueta <body>.

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Element Plus</title>
    <!--preload-links-->
  </head>
  <body>
    <!--app-teleports-->
    <div id="app"><!--app-html--></div>
    <script type="module" src="/src/entry-client.js"></script>
  </body>
</html>

TIP

Si modifica el atributo Namespace o append-to, necesita ajustar el valor #el-popper-container-.

// src/entry-server.js (irrelevant code omitted)
import { renderToString } from 'vue/server-renderer'
import { createApp } from './main'

export async function render(url, manifest) {
  // ...
  const ctx = {}
  const html = await renderToString(app, ctx)
  const preloadLinks = renderPreloadLinks(ctx.modules, manifest)
  const teleports = renderTeleports(ctx.teleports)

  return [html, preloadLinks, teleports]
}

function renderTeleports(teleports) {
  if (!teleports) return ''
  return Object.entries(teleports).reduce((all, [key, value]) => {
    if (key.startsWith('#el-popper-container-')) {
      return `${all}<div id="${key.slice(1)}">${value}</div>`
    }
    return all
  }, teleports.body || '')
}
// server.js or prerender.js (irrelevant code omitted)
const [appHtml, preloadLinks, teleports] = await render(url, manifest)

const html = template
  .replace('<!--preload-links-->', preloadLinks)
  .replace('<!--app-html-->', appHtml)
  .replace(/(\n|\r\n)\s*<!--app-teleports-->/, teleports)