第五章:Vue3 状态管理(Pinia 从入门到实战)

一、入门:为什么用 Pinia?核心概念与环境准备

  1. Pinia 替代 Vuex 的核心原因:Pinia 是 Vue 官方推荐的状态管理库,相比 Vuex 3/4,它无需模块化嵌套(单个 Store 即可独立使用)、天然支持 TypeScript、API 更简洁(去掉 mutations,直接用 actions 处理同步/异步),且体积更小、性能更优。
  2. 核心概念:Pinia 只有两个核心概念——Store(存储状态的容器,每个 Store 对应一个业务模块,如用户 Store、购物车 Store)和 Actions(修改状态的方法,支持同步和异步,替代 Vuex 的 mutations 和 actions)。
  3. 环境搭建:
    • 安装:在 Vue3 项目中执行 npm install pinia
    • 全局注册:在 main.js 中引入并创建 Pinia 实例,挂载到 app:
    import { createApp } from 'vue'
    import { createPinia } from 'pinia'
    import App from './App.vue'
    const app = createApp(App)
    app.use(createPinia()) // 注册 Pinia
    app.mount('#app')
    

二、精通:Store 创建与状态操作全流程

  1. 创建 Store(以“用户 Store”为例):
    • src/store 文件夹下新建 userStore.js,用 defineStore 定义 Store,第一个参数是唯一 ID(必须,用于 Pinia 内部识别),第二个参数是配置对象:
    import { defineStore } from 'pinia'
    // 函数名建议用 useXXXStore 格式(规范)
    export const useUserStore = defineStore('user', {
      // state:存储状态(类似组件的 data)
      state: () => ({
        username: '',
        token: '',
        isLogin: false
      }),
      // actions:修改状态的方法(支持同步/异步,类似组件的 methods)
      actions: {
        // 同步修改:登录成功更新状态
        loginSuccess(userInfo) {
          this.username = userInfo.username // 直接通过 this 访问 state
          this.token = userInfo.token
          this.isLogin = true
          // 持久化存储:将 token 存到 localStorage
          localStorage.setItem('token', userInfo.token)
        },
        // 异步操作:模拟接口请求登录
        async login(data) {
          // 模拟 axios 请求
          // const res = await axios.post('/api/login', data)
          const res = { code: 200, data: { username: data.username, token: 'xxx123' } }
          if (res.code === 200) {
            this.loginSuccess(res.data) // 调用同步 action 更新状态
            return true
          }
          return false
        },
        // 退出登录:清空状态
        logout() {
          this.username = ''
          this.token = ''
          this.isLogin = false
          localStorage.removeItem('token')
        }
      },
      // getters:计算属性(类似组件的 computed,自动缓存)
      getters: {
        // 拼接欢迎语
        welcomeMsg: (state) => `欢迎回来,${state.username || '游客'}`
      }
    })
    
  2. 状态操作(在组件中使用 Store):
    • 引入并创建 Store 实例:import { useUserStore } from '@/store/userStore'const userStore = useUserStore()
    • 访问 state:直接用 userStore.username(无需像 Vuex 一样用 state.user.username)。
    • 调用 actions:await userStore.login({ username: 'test', password: '123' })(异步 action 需加 await)。
    • 访问 getters:userStore.welcomeMsg(直接调用,无需加括号)。
  3. 状态重置与修改技巧:
    • 重置状态:userStore.$reset()(将 state 恢复到初始值,适合退出登录等场景)。
    • 批量修改状态:用 $patch 方法,避免多次修改触发多次更新,例:userStore.$patch({ username: 'newName', isLogin: true })

三、实战:用 Pinia 实现“购物车功能”

  1. 需求拆解:实现“添加商品到购物车”“修改商品数量”“删除购物车商品”“计算购物车总价”“选择/取消选择商品”功能,且购物车状态需在多个组件(商品详情页、购物车页、导航栏购物车图标)间共享。
  2. 核心实现(创建 cartStore.js):
    • state 定义:cartList: [](存储购物车商品,每个商品包含 id、name、price、count、checked)。
    • actions 实现:
      • addCart(goods):先判断商品是否已在购物车,已存在则增加 count,不存在则添加到 cartList(设置默认 count=1、checked=true)。
      • updateCount(id, count):根据商品 id 找到对应项,更新 count(count 不能小于 1)。
      • deleteCart(id):过滤掉 id 对应的商品,更新 cartList。
      • toggleCheck(id):切换单个商品的 checked 状态;toggleAllCheck(checked):全选/全不选所有商品。
    • getters 实现:
      • totalPrice:计算选中商品的总价(遍历 cartList,累加 checked 为 true 的商品 price * count)。
      • selectedCount:计算选中商品的总数量。
      • isAllChecked:判断是否所有商品都被选中(cartList 不为空且 every 商品 checked 为 true)。
  3. 组件中使用:
    • 商品详情页:点击“加入购物车”,调用 cartStore.addCart(goodsInfo),并提示添加成功。
    • 购物车页:通过 cartStore.cartList 渲染商品列表,绑定“数量修改”“删除”“选择”按钮,调用对应 actions;用 cartStore.totalPrice 显示总价,用 cartStore.isAllChecked 控制全选框。
    • 导航栏:用 cartStore.selectedCount 显示购物车图标上的数字,实现状态实时同步。
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐