SSR 兼容性 
通过使用 Vue 的服务器端渲染 (SSR) 功能,VitePress 能够在生产构建期间在 Node.js 中预渲染应用程序。这意味着主题组件中的所有自定义代码都需要考虑 SSR 兼容性。
Vue 官方文档的 SSR 部分提供了更多有关 SSR 是什么,SSR / SSG 之间的关系以及编写 SSR 友好代码的常见注意事项等信息。原则上只在 Vue 组件的 beforeMount 或 mounted 钩子中访问 browser / DOM API。
<ClientOnly> 
如果你正在使用或演示不支持 SSR 的组件 (例如,包含自定义指令),则可以将它们包装在内置的 <ClientOnly> 组件中:
md
<ClientOnly>
  <NonSSRFriendlyComponent />
</ClientOnly><ClientOnly>
  <NonSSRFriendlyComponent />
</ClientOnly>1
2
3
2
3
在导入时访问浏览器 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>1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
条件导入 
你也可以使用 i 标志 (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
	})
}1
2
3
4
5
2
3
4
5
因为 Theme.enhanceApp 可以是异步的,所以你可以有条件地导入并注册访问浏览器 API 的 Vue 插件:
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)
		}
	},
}// .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)
		}
	},
}1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
如果你使用 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)
		}
	},
} satisfies Theme// .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)
		}
	},
} satisfies Theme1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
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>1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
你还可以将 props/children/slots 传递给目标组件:
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 are passed to 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')],
		},
	],
	// callback after the component is loaded, can be async
	() => {
		console.log(clientCompRef.value)
	},
)
</script>
<template>
	<ClientComp />
</template><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 are passed to 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')],
		},
	],
	// callback after the component is loaded, can be async
	() => {
		console.log(clientCompRef.value)
	},
)
</script>
<template>
	<ClientComp />
</template>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
The target component will only be imported in the mounted hook of the wrapper component.