Skip to content

Совместимость с SSR

VitePress предварительно рендерит приложение в Node.js во время производственной сборки, используя возможности Vue по рендерингу на стороне сервера (SSR). Это означает, что весь пользовательский код в компонентах темы подлежит проверке на совместимость с SSR.

Глава Рендеринг на стороне сервера в документации Vue содержит более подробную информацию о том, что такое SSR, взаимосвязь между SSR и SSG, а также общие указания по написанию кода, дружественного к SSR. Правило заключается в том, чтобы обращаться к API браузера / DOM только в хуках beforeMount или mounted компонентов Vue.

<ClientOnly>

Если вы используете или демонстрируете компоненты, которые не являются SSR-дружественными (например, содержат пользовательские директивы), вы можете обернуть их внутри встроенного компонента <ClientOnly>:

md
<ClientOnly>
  <NonSSRFriendlyComponent />
</ClientOnly>

Библиотеки, обращающиеся к API браузера при импорте

Некоторые компоненты или библиотеки получают доступ к API браузера при импорте. Чтобы использовать код, предполагающий наличие среды браузера при импорте, необходимо динамически импортировать их.

Импорт в хуке onMounted

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

onMounted(() => {
  import('./lib-that-access-window-on-import').then((module) => {
    // используем код
  })
})
</script>

Условный импорт

Вы также можете условно импортировать зависимость с помощью флага import.meta.env.SSR (часть env-переменных Vite):

js
if (!import.meta.env.SSR) {
  import('./lib-that-access-window-on-import').then((module) => {
    // используем код
  })
}

Поскольку Theme.enhanceApp может быть асинхронным, вы можете условно импортировать и регистрировать плагины Vue, которые получают доступ к API браузера при импорте:

js
// .vitepress/theme/index.js
/** @type {import('vitepress').Theme} */
export default {
  // ...
  async enhanceApp({ app }) {
    if (!import.meta.env.SSR) {
      const plugin = await import('plugin-that-access-window-on-import')
      app.use(plugin.default)
    }
  }
}

Если вы используете TypeScript:

ts
// .vitepress/theme/index.ts
import type { Theme } from 'vitepress'

export default {
  // ...
  async enhanceApp({ app }) {
    if (!import.meta.env.SSR) {
      const plugin = await import('plugin-that-access-window-on-import')
      app.use(plugin.default)
    }
  }
} satisfies Theme

defineClientComponent

VitePress предоставляет удобный помощник для импорта компонентов Vue, которые получают доступ к API браузера при импорте.

vue
<script setup>
import { defineClientComponent } from 'vitepress'

const ClientComp = defineClientComponent(() => {
  return import('component-that-access-window-on-import')
})
</script>

<template>
  <ClientComp />
</template>

Вы также можете передавать параметры/дочерние элементы/слоты целевому компоненту:

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

const clientCompRef = ref(null)
const ClientComp = defineClientComponent(
  () => import('component-that-access-window-on-import'),

  // args передаются в функцию h() - https://ru.vuejs.org/api/render-function.html#h
  [
    {
      ref: clientCompRef
    },
    {
      default: () => 'default slot',
      foo: () => h('div', 'foo'),
      bar: () => [h('span', 'one'), h('span', 'two')]
    }
  ],

  // обратный вызов после загрузки компонента, может быть асинхронным
  () => {
    console.log(clientCompRef.value)
  }
)
</script>

<template>
  <ClientComp />
</template>

Целевой компонент будет импортирован только в смонтированный хук компонента-обёртки.

Опубликовано под лицензией MIT.