为什么需要状态管理?
在 Vue 应用中,当我们需要在多个组件之间共享状态时,简单的父子组件通信可能会变得复杂和难以维护。状态管理工具提供了一个集中管理共享状态的解决方案。
什么时候需要状态管理
-
多个组件共享状态
- 用户信息需要在多个页面显示
- 购物车数据需要在多个组件更新
-
组件间通信复杂
- 多层级组件通信
- 兄弟组件通信频繁
-
需要持久化数据
- 页面刷新后保持状态
- 数据需要存储到本地
Vuex:Vue 的经典状态管理方案
核心概念
import { createStore } from 'vuex'
export default createStore({ state: { count: 0, todos: [], user: null }})
// 组件中使用import { useStore } from 'vuex'
const store = useStore()console.log(store.state.count)
const store = createStore({ state: { todos: [ { id: 1, text: '学习 Vue', done: true }, { id: 2, text: '学习 Vuex', done: false } ] }, getters: { doneTodos: (state) => { return state.todos.filter(todo => todo.done) }, doneTodosCount: (state, getters) => { return getters.doneTodos.length } }})
const store = createStore({ state: { count: 0 }, mutations: { increment(state) { state.count++ }, incrementBy(state, payload) { state.count += payload.amount } }})
// 组件中使用store.commit('increment')store.commit('incrementBy', { amount: 10 })
const store = createStore({ state: { user: null }, mutations: { setUser(state, user) { state.user = user } }, actions: { async login({ commit }, credentials) { try { const user = await api.login(credentials) commit('setUser', user) return user } catch (error) { console.error('登录失败:', error) throw error } } }})
// 组件中使用await store.dispatch('login', { username: 'admin', password: '123456'})
模块化
对于大型应用,我们可以将 store 分割成模块:
export default { namespaced: true, state: { user: null, token: null }, mutations: { setUser(state, user) { state.user = user }, setToken(state, token) { state.token = token } }, actions: { async login({ commit }, credentials) { const { user, token } = await api.login(credentials) commit('setUser', user) commit('setToken', token) } }, getters: { isLoggedIn: state => !!state.token }}
// store/index.jsimport { createStore } from 'vuex'import user from './modules/user'
export default createStore({ modules: { user }})
// 组件中使用store.dispatch('user/login', credentials)console.log(store.getters['user/isLoggedIn'])
Pinia:新一代状态管理工具
Pinia 是 Vue 官方推荐的新一代状态管理工具,它提供了更简单的 API 和更好的 TypeScript 支持。
基础用法
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', { // 状态 state: () => ({ count: 0, name: 'Counter' }),
// 计算属性 getters: { doubleCount: (state) => state.count * 2 },
// 方法 actions: { increment() { this.count++ }, async fetchCount() { const result = await api.getCount() this.count = result } }})
<template> <div> <p>Count: {{ counter.count }}</p> <p>Double: {{ counter.doubleCount }}</p> <button @click="counter.increment">+1</button> <button @click="counter.fetchCount">获取远程数据</button> </div></template>
<script setup>import { useCounterStore } from '@/stores/counter'
const counter = useCounterStore()</script>
组合式 API 风格
Pinia 也支持使用组合式 API 风格定义 store:
import { ref, computed } from 'vue'import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', () => { // 状态 const user = ref(null) const token = ref(null)
// 计算属性 const isLoggedIn = computed(() => !!token.value)
// 方法 async function login(credentials) { const response = await api.login(credentials) user.value = response.user token.value = response.token }
function logout() { user.value = null token.value = null }
return { user, token, isLoggedIn, login, logout }})
Pinia 的优势
-
更简单的 API
- 无需手动添加模块
- 无需嵌套模块命名空间
- 更接近 Vue 组合式 API 的写法
-
更好的 TypeScript 支持
- 自动类型推导
- IDE 智能提示
- 更少的类型标注
-
更轻量级
- 体积更小
- 性能更好
- 代码更简洁
最佳实践
-
Store 的组织
- 按功能模块拆分 store
- 避免过度拆分
- 保持状态树的扁平结构
-
状态更新原则
- 同步更新使用 mutations/直接修改
- 异步操作使用 actions
- 避免在组件中直接修改状态
-
性能优化
- 合理使用计算属性
- 避免存储大量数据
- 按需加载模块
选择建议
-
使用 Vuex 的场景
- 大型复杂应用
- 需要严格的状态管理
- 团队已经熟悉 Vuex
-
使用 Pinia 的场景
- 新项目
- 中小型应用
- 使用 TypeScript
- 需要更简单的 API
总结
状态管理是构建大型 Vue 应用的重要工具:
- Vuex 提供了经典且成熟的解决方案
- Pinia 带来了更现代的开发体验
- 选择合适的工具取决于项目需求
无论选择哪种方案,关键是遵循最佳实践,保持代码的可维护性和性能。