状态管理
🎯 引言
学会这篇文章,你能在 Nuxt 项目里合理地管理共享状态。你会掌握 useState 的用法,理解它和 Pinia 的分工,并知道在 SSR 场景下管理状态要注意什么。
🧱 为什么需要状态管理
当多个组件需要共享同一份数据时,如果只靠 props 传递和事件通知,代码会很快变得复杂。
状态管理的作用就是:把共享数据放到一个公共的地方,任何组件都能直接读取或修改。Nuxt 提供了两种常用方案:
| 方案 | 适用场景 | 复杂度 |
|---|---|---|
useState | 简单跨组件共享状态 | 低 |
| Pinia | 大型应用、复杂业务逻辑 | 中 |
useState 更轻量。🚀 useState:Nuxt 内置的轻量状态
useState 是 Nuxt 提供的一个组合式函数,用来创建响应式且支持 SSR 的共享状态。
export function useCounter() {
return useState('counter', () => 0);
}
在组件中使用:
<template>
<div>
<p>计数:{{ count }}</p>
<button @click="count++">加一</button>
</div>
</template>
<script setup>
const count = useCounter();
</script>
useState 第一个参数是全局唯一的 key,不同状态不能用相同的 key,否则数据会互相覆盖。🔄 跨组件共享状态
因为 useState 通过 key 全局共享,所以多个组件使用同一个 key 时,读取到的是同一份数据。
<template>
<button @click="count++">加一</button>
</template>
<script setup>
const count = useState('counter', () => 0);
</script>
<template>
<p>当前计数:{{ count }}</p>
</template>
<script setup>
const count = useState('counter', () => 0);
</script>
点击 AddButton 的按钮,ShowCount 里的数字会同步变化。
useState 封装到 composables/ 里的函数中,这样组件不需要关心 key,也不容易写错。⚡ Pinia:复杂状态的首选
如果状态逻辑比较复杂,比如需要 actions、getters、模块化,建议用 Pinia。Nuxt 对 Pinia 有官方支持。
安装 Pinia:
npm install @pinia/nuxt pinia
@pinia/nuxt 和 pinia 均为较新版本,与 Nuxt ^3.16.0 兼容。在 nuxt.config.ts 中添加模块:
export default defineNuxtConfig({
modules: ['@pinia/nuxt'],
});
创建 Store:
import { defineStore } from 'pinia';
export const useUserStore = defineStore('user', {
state: () => ({
name: '游客',
isLogin: false,
}),
actions: {
login(name) {
this.name = name;
this.isLogin = true;
},
},
});
在组件中使用:
<template>
<div>
<p>用户名:{{ userStore.name }}</p>
<button v-if="!userStore.isLogin" @click="userStore.login('小明')">登录</button>
</div>
</template>
<script setup>
const userStore = useUserStore();
</script>
🆚 useState 和 Pinia 怎么选
| 场景 | 推荐方案 |
|---|---|
| 临时共享一个小状态,比如主题色、弹窗开关 | useState |
| 需要 actions、getters、模块拆分 | Pinia |
| 状态逻辑复杂,多个组件频繁读写 | Pinia |
| 只想快速在几个组件间同步数据 | useState |
useState 可以减少样板代码。🪤 SSR 下的状态注意点
useState 创建的状态会自动注入到服务端渲染的 HTML 中,然后客户端激活时复用。这意味着首屏就能看到正确状态,不会闪烁。
但要注意:不要在 useState 里放无法序列化的数据,比如函数、DOM 节点、循环引用对象等,否则 SSR 会报错。
// ❌ 错误:函数不能序列化
const fn = useState('fn', () => () => {});
// ✅ 正确:只放普通对象、数组、基础类型
const user = useState('user', () => ({ name: '小明', age: 18 }));
🧾 小节总结
- Nuxt 中管理共享状态有
useState和 Pinia 两种常用方案。 useState适合简单状态,通过全局 key 共享,自动支持 SSR。- Pinia 适合复杂状态,通过
@pinia/nuxt模块集成到 Nuxt 中。 - 建议把
useState封装到composables/里,避免组件直接写 key。 - SSR 场景下,
useState里的数据必须可序列化。
❓ 知识问答
Q1:useState 和 Vue 的 ref 有什么区别?
useState 内部就是 ref,但它额外支持 SSR 数据注入和跨组件共享。
Q2:两个组件使用同一个 useState key,数据会同步吗?
会。useState 通过 key 在全局维护同一份状态。
Q3:Nuxt 里必须用 Pinia 吗?
不是必须。简单项目用 useState 就够了,复杂项目再用 Pinia。
Q4:为什么 useState 里的数据要可序列化?
因为 SSR 时数据要从服务端传到客户端,不能序列化的数据无法安全传递。
🧪 小练习
完成一个主题切换功能:
- 在
app/composables/useTheme.js中用useState创建一个theme状态,默认值为'light'。 - 创建
app/components/ThemeToggle.vue,点击按钮在'light'和'dark'之间切换。 - 创建
app/components/ThemeDisplay.vue,显示当前主题。 - 在
app/pages/index.vue中同时使用这两个组件,验证状态同步。
export function useTheme() {
// 请在这里创建 useState
}
<template>
<button @click="toggle">切换主题</button>
</template>
<script setup>
// 请在这里获取 theme 并实现切换
</script>
🎉 恭喜你已经掌握了 Nuxt 的状态管理!下一篇我们会学习 Nuxt 的配置与部署,理解 nuxt.config.ts 和不同渲染模式的选择。
