Skip to content

解决 VitePress 由于引入不支持 SSR 的组件从而造成构建失败的问题

什么是 <ClientOnly>

<ClientOnly> 是 VuePress / VitePress 提供的一个特殊组件,用于确保其内容仅在客户端(浏览器)环境中渲染,而在服务器端渲染 (SSR) 阶段则不会进行渲染。

通过 <ClientOnly> 包裹的内容只有在客户端渲染时才会显示,这有助于防止在服务器渲染时尝试使用浏览器特有的 API 导致的错误。

服务端渲染 (SSR) 问题

在 SSR 模式下,页面首先在服务器端进行渲染,然后将预渲染的 HTML 发送到客户端,客户端接收到 HTML 后再进行接管。这个过程有助于提高页面的首次加载速度和 SEO 友好性。然而,并不是所有的 JavaScript 代码都能在服务器环境中运行,例如,直接操作 DOM、使用 windowdocument 对象等。

典型问题

当包含浏览器特有 API 的组件在 SSR 环境下渲染时,通常会抛出类似以下的错误:

  • ReferenceError: window is not defined
  • ReferenceError: document is not defined
  • 第三方库依赖于浏览器环境而无法正常工作

例如,下述代码在 SSR 环境下将会报错,因为 navigator 这个 API 只有在浏览器环境中存在:

vue
<template>
  <div>
    <p>User Agent: {{ userAgent }}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      userAgent: navigator.userAgent,
    };
  },
};
</script>

<ClientOnly> 的作用

为了解决上述问题,VuePress / VitePress 提供了 <ClientOnly> 组件。通过将依赖浏览器 API 的部分代码包裹在 <ClientOnly> 中,可以避免在 SSR 过程中执行这些代码,防止出现错误。

示例

以下是一个使用 <ClientOnly> 的示例:

vue
<template>
  <div>
    <client-only>
      <p>User Agent: {{ userAgent }}</p>
    </client-only>
  </div>
</template>

<script>
export default {
  data() {
    return {
      userAgent: '',
    };
  },
  mounted() {
    this.userAgent = navigator.userAgent;
  },
};
</script>

在这个例子中,<p>User Agent: </p> 只有在浏览器环境下才会被渲染和执行。这确保了在服务器渲染阶段不会因 navigator 未定义而报错。

在 VitePress 中使用 <ClientOnly>

VitePress 是基于 Vite 构建的静态网站生成器,支持 Vue 3。与 VuePress 类似,VitePress 也可以使用 <ClientOnly> 组件来解决同样的问题。

使用示例

在 VitePress 中使用 <ClientOnly> 组件的方式与在 VuePress 中使用是相同的。以下是一个具体的例子:

vue
<template>
  <div>
    <h1>Welcome to VitePress</h1>
    <client-only>
      <example-chart></example-chart>
    </client-only>
  </div>
</template>

<script>
import ExampleChart from './ExampleChart.vue'; // 假设 ExampleChart 组件依赖于浏览器 API

export default {
  components: {
    ExampleChart,
  },
};
</script>

在这个例子中,<ExampleChart> 组件只有在客户端渲染时才会被挂载和执行。这样就可以避免在服务器渲染阶段因浏览器特有 API 不可用而导致的错误。

总结

在使用 VuePress 或 VitePress 构建静态网站时,可能会遇到一些组件依赖于浏览器环境的问题。通过使用 <ClientOnly> 组件,可以确保这些仅依赖于客户端环境的代码不会在服务端渲染时执行,从而提高应用的健壮性和可维护性。