Совместимость с SSR
VitePress предварительно рендерит приложение в Node.js во время продакшн-сборки, используя возможности серверного рендеринга (SSR) Vue. Это означает, что весь пользовательский код в компонентах темы должен быть совместим с SSR.
Раздел SSR в официальной документации Vue предоставляет больше контекста о том, что такое SSR, отношения между SSR и SSG, и общие замечания о написании кода, совместимого с SSR. Как правило, доступ к API браузера / DOM следует осуществлять только в хуках beforeMount
или mounted
компонентов Vue.
<ClientOnly>
Если вы используете или демонстрируете компоненты, которые не совместимы с SSR (например, содержат пользовательские директивы), вы можете обернуть их во встроенный компонент <ClientOnly>
:
<ClientOnly>
<NonSSRFriendlyComponent />
</ClientOnly>
Библиотеки, обращающиеся к API браузера при импорте
Некоторые компоненты или библиотеки обращаются к API браузера при импорте. Чтобы использовать код, который предполагает наличие браузерной среды при импорте, вам нужно динамически импортировать их.
Импорт в хуке Mounted
<script setup>
import { onMounted } from 'vue'
onMounted(() => {
import('./lib-that-access-window-on-import').then((module) => {
// использовать код
})
})
</script>
Условный импорт
Вы также можете условно импортировать зависимость, используя флаг import.meta.env.SSR
(часть переменных среды Vite):
if (!import.meta.env.SSR) {
import('./lib-that-access-window-on-import').then((module) => {
// использовать код
})
}
Поскольку Theme.enhanceApp
может быть асинхронным, вы можете условно импортировать и регистрировать плагины Vue, которые обращаются к API браузера при импорте:
// .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:
// .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 браузера при импорте.
<script setup>
import { defineClientComponent } from 'vitepress'
const ClientComp = defineClientComponent(() => {
return import('component-that-access-window-on-import')
})
</script>
<template>
<ClientComp />
</template>
Вы также можете передать props/children/slots целевому компоненту:
<script setup>
import { ref } from 'vue'
import { defineClientComponent } from 'vitepress'
const clientCompRef = ref(null)
const ClientComp = defineClientComponent(
() => import('component-that-access-window-on-import'),
// аргументы передаются в h() - https://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>
Целевой компонент будет импортирован только в хуке mounted обертывающего компонента.