在 Nuxt 3 服务端渲染期间,window is not defined 报错是因为服务端 Node.js 环境中不存在浏览器 DOM 对象。最推荐的处理方向是将访问 window 的代码移入 onMounted 生命周期钩子,或使用 .client.vue 后缀强制组件仅在客户端渲染,适用场景为依赖浏览器 API 的功能,风险边界是需注意服务端与客户端 HTML 结构不一致可能触发的水合警告。
先说结论:该报错源于服务端代码尝试访问浏览器专属对象,必须限制相关逻辑仅在客户端执行。
- 先确认:检查报错堆栈,定位具体哪一行代码访问了 window 或 document
- 先处理:使用 onMounted 包裹逻辑,或将组件重命名为 .client.vue
- 再验证:重启开发服务器,确认控制台不再出现 ReferenceError 且页面无水合警告
命令速用版
此类问题主要通过代码修改解决,无需执行终端命令,以下是快速处理思路:
- 找到访问 window 的代码位置
- 若是逻辑代码,放入 onMounted(() => { ... })
- 若是整个组件依赖 DOM,将文件名改为 xxx.client.vue
- 若是模板差异,使用 <ClientOnly> 组件包裹
为什么会这样
Nuxt 3 默认开启服务端渲染,组件初始渲染发生在 Node.js 服务器环境中。
Node.js 环境没有浏览器提供的 window、document 或 localStorage 对象。当代码在服务端执行阶段直接读取这些变量时,JavaScript 引擎无法找到定义,从而抛出 ReferenceError。只有当页面加载到浏览器后,这些对象才会存在。
分步处理
步骤 1:定位报错代码
查看终端报错堆栈,找到具体文件路径和行号。常见于 composables、plugins 或组件的 setup 函数顶层。
步骤 2:使用 onMounted 包裹逻辑
在 script setup 中,将涉及 window 的代码移入 onMounted 钩子。onMounted 仅在客户端浏览器中执行。
<script setup>
import { onMounted } from 'vue'
onMounted(() => {
console.log(window.innerWidth)
})
</script>步骤 3:使用 .client.vue 后缀
如果整个组件都依赖浏览器 API,将组件文件名从 Component.vue 改为 Component.client.vue。Nuxt 会跳过该组件的服务端渲染。
步骤 4:使用 <ClientOnly> 组件
在模板中,只让特定部分在客户端渲染。服务端会渲染插槽的 fallback 内容。
<template>
<ClientOnly>
<BrowserSpecificComponent />
</ClientOnly>
</template>怎么验证是否生效
重启开发服务器,观察终端是否还有 ReferenceError。打开浏览器控制台,确认没有 hydration mismatch 警告。检查页面功能是否正常。
常见坑
1. 在插件顶层直接访问 window:plugins 代码会在服务端运行,必须使用 defineNuxtPlugin 并在内部判断 process.client。
2. 第三方库未兼容 SSR:引入某些 UI 库时,需在 nuxt.config.ts 中配置 transpile 或使用 ClientOnly 包裹。
3. 全局变量污染:不要在 setup 顶层将 window 对象赋值给响应式变量,这会在服务端触发报错。
常见问题
process.client 和 import.meta.client 有什么区别?
两者都可用于判断当前环境,import.meta.client 是 Vite 标准写法,process.client 是 Nuxt 提供的兼容写法,推荐优先使用 import.meta.client。
为什么用了 onMounted 还是报错?
检查是否在 setup 顶层直接调用了访问 window 的函数,确保该函数调用本身也位于 onMounted 回调内部。
如何在全局插件中安全使用 window?
在 defineNuxtPlugin 内部,将逻辑包裹在 if (process.client) 判断中,或确保插件仅在客户端加载。