Skip to content

VuePress 打包报错 ReferenceError document is not defined 的原因与解决方案

约 703 字大约 2 分钟

Vue

2025-04-02

在使用 VuePress 搭建文档网站的过程中,我们经常会想引入一些交互性的插件,比如 cursor-effects 提供的趣味鼠标效果。然而在构建(build)时却可能遇到如下报错:

ReferenceError: document is not defined

这篇文章将深入分析这个问题的根本原因,并提供一种优雅的解决方案。


🚨 问题现象

在开发环境 (vuepress dev) 中一切正常,但当执行打包命令 vuepress build 时却报错:

ReferenceError: document is not defined

通常,这种报错会出现在你直接在代码中调用了 document 或其他浏览器 API 的情况下,例如:

import 'cursor-effects'

new springyEmojiCursor() // 报错行

🧠 原因分析

VuePress 在打包时采用的是 服务端渲染(Server-Side Rendering, SSR),构建过程是由 Node.js 运行的。而 Node.js 是运行在服务端的环境,不存在 documentwindownavigator 等浏览器特有的对象。

所以,如果你在构建时执行了依赖 DOM 的代码(比如直接初始化动画效果或操作 DOM),就会导致构建失败。


✅ 解决方案

解决思路非常明确:确保所有依赖浏览器环境的代码只在客户端执行,也就是说,只在页面真正加载后、浏览器环境已就绪时再执行。

情况 1:在页面组件中引入插件

如果你只想在某个具体页面或组件中使用,比如 Home.vue,可以这样做:

import { onMounted } from 'vue'

onMounted(() => {
  import('cursor-effects').then((m) => {
    new m.springyEmojiCursor({ emoji: '🔥' })
  })
})

因为 onMounted 只在客户端执行,构建时不会触发这段代码,自然不会报错。


情况 2:全局使用插件

如果希望整个文档站点都启用这个效果,应在 VuePress 提供的 client.ts 中注册。

VuePress 2 提供了一个增强入口文件:docs/.vuepress/client.ts,我们可以在这里使用 defineClientConfig 来定义全局客户端配置:

// docs/.vuepress/client.ts

import { onMounted } from 'vue'
import { defineClientConfig } from 'vuepress/client'

export default defineClientConfig({
  setup() {
    onMounted(() => {
      // 可安全使用浏览器 API
      document.querySelector('#app')

      import('cursor-effects').then(function (m) {
        new m.springyEmojiCursor({ emoji: "🤣" })
      })
    })
  },
})

说明:

  • defineClientConfig:VuePress 提供的客户端配置函数,类似于你在 Vue 应用中使用的根组件设置。
  • setup():可使用组合式 API(如 onMountedref 等)官方文档
  • onMounted():确保 DOM 已加载,此时使用 document 是安全的。
  • import():使用动态导入的方式,确保只有在浏览器中执行。

💡 小贴士

  • 永远不要在顶层作用域中直接使用 documentwindow 等浏览器 API;
  • 使用 onMounted() 包裹你的 DOM 操作逻辑;
  • 如果你用的是第三方依赖,确保它不会在 SSR 构建时执行副作用代码。

更新日志

2025/6/16 07:14
查看所有更新日志
  • 23359-new article