Skip to content

SSR 兼容性

通过使用 Vue 的服务器端渲染 (SSR) 功能,VitePress 能够在生产构建期间在 Node.js 中预渲染应用程序。这意味着主题组件中的所有自定义代码都需要考虑 SSR 兼容性。

Vue 官方文档的 SSR 部分提供了更多有关 SSR 是什么,SSR / SSG 之间的关系以及编写 SSR 友好代码的常见注意事项等信息。原则上只在 Vue 组件的 beforeMountmounted 钩子中访问 browser / DOM API。

<ClientOnly>

如果你正在使用或演示不支持 SSR 的组件 (例如,包含自定义指令),则可以将它们包装在内置的 <ClientOnly> 组件中:

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

在导入时访问浏览器 API 的库

一些组件或库在导入时访问浏览器 API。要使用假定在导入时处于浏览器环境的代码,你需要动态导入它们。

在 mounted 钩子中导入

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

onMounted(() => {
  import('./lib-that-access-window-on-import').then((module) => {
    // use code
  })
})
</script>
<script setup>
import { onMounted } from 'vue'

onMounted(() => {
  import('./lib-that-access-window-on-import').then((module) => {
    // use code
  })
})
</script>

条件导入

你也可以使用 import.meta.env.SSR 标志 (Vite 环境变量的一部分) 来有条件地导入依赖项:

js
if (!import.meta.env.SSR) {
  import('./lib-that-access-window-on-import').then((module) => {
    // use code
  })
}
if (!import.meta.env.SSR) {
  import('./lib-that-access-window-on-import').then((module) => {
    // use code
  })
}

因为 Theme.enhanceApp 可以是异步的,所以你可以有条件地导入并注册访问浏览器 API 的 Vue 插件:

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

defineClientComponent {#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>
<script setup>
import { defineClientComponent } from 'vitepress'

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

<template>
  <ClientComp />
</template>

目标组件只会在包装组件的 mounted 钩子中导入。

Released under the MIT License.