在 Nuxt 4(以及 Nuxt 3)项目中,服务端渲染(SSR)环境下确实不能直接使用 window 对象。
这是因为 Nuxt 的 SSR 机制会在服务器(Node.js 环境)中先执行一次 Vue 应用代码来生成 HTML。在服务器环境中,不存在浏览器特有的 window、document 等对象。如果代码在服务端执行时尝试访问 window,就会报错 ReferenceError: window is not defined。
针对 pinia-plugin-persistedstate 插件,解决这个问题的核心思路是:确保与服务端无关的代码(如 localStorage 操作)只在客户端运行。
以下是具体的解决方案和最佳实践:
✅ 解决方案:使用 .client.ts 后缀注册插件
这是 Nuxt 推荐的处理方式。通过在插件文件名后加上 .client 后缀(例如 pinia-persist.client.ts),Nuxt 会自动识别该插件仅在客户端加载和运行,从而完全避开服务端的执行环境。
1. 安装依赖
bash
npm install pinia-plugin-persistedstate
2. 创建客户端专用插件
在 plugins 目录下创建文件 plugins/pinia-persist.client.ts:
typescript
// plugins/pinia-persist.client.ts
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
export default defineNuxtPlugin((nuxtApp) => {
// 将插件注册到 Pinia 实例中
// 由于文件名包含 .client,这行代码只会在浏览器端执行,安全使用 window/localStorage
nuxtApp.$pinia.use(piniaPluginPersistedstate)
})
3. 配置 Store
在你的 Store 中配置 persist 选项。虽然插件只在客户端运行,但在定义 Store 时(该文件会被服务端打包),我们仍然建议加上 process.client 判断,以确保配置的严谨性。
typescript
// stores/user.ts
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
state: () => ({
name: '',
token: ''
}),
actions: {
// ...
},
// 配置持久化
persist: {
enabled: true,
// 关键点:使用 process.client 判断,防止服务端打包时报错
storage: process.client ? localStorage : undefined,
}
})
💡 原理深度解析
为了让你更清楚为什么这样做,我们可以看一下 Nuxt 的处理流程:
| 步骤 | 服务端 (Server) | 客户端 (Client) | 备注 |
|---|---|---|---|
| 1. 初始请求 | Node.js 运行代码 | 无 | 此时没有 window 对象 |
| 2. 插件加载 | 跳过 .client.ts 文件 |
加载 .client.ts 文件 |
插件只在浏览器初始化 |
| 3. Store 定义 | 加载 Store 定义文件 | 加载 Store 定义文件 | 所以 Store 文件内需 process.client 判断 |
| 4. 状态恢复 | 生成 HTML (不含持久化状态) | 读取 localStorage 恢复状态 |
插件在客户端自动处理水合 (Hydration) |
⚠️ 常见误区提醒
- **不要在全局作用域直接使用 **
localStorage:
即使在 Store 文件中,也不要直接写storage: localStorage而不加判断,因为 Store 文件本身会被服务端打包读取。虽然插件逻辑不运行,但变量求值可能会在服务端触发。- ❌ 错误:
persist: { storage: localStorage } - ✅ 正确:
persist: { storage: process.client ? localStorage : undefined }
- ❌ 错误:
- 不要试图在服务端做持久化 :
pinia-plugin-persistedstate的设计初衷是基于浏览器 API (localStorage/sessionStorage) 的。如果你需要在服务端持久化(例如写入 Redis 或 Cookie),通常需要编写自定义的 Nuxt 插件或使用 Nuxt 的useState配合cookie选项,而不是依赖这个插件。
通过以上配置,你就可以在 Nuxt 4 项目中安全地使用 Pinia 持久化插件,既解决了 window is not defined 的报错,又能实现刷新页面状态不丢失的功能。