Skip to content
第 11 章 前端 ⏱ 12 分钟阅读

第 11 章:Pinia 状态管理 ​

学习目标 ​

  • 安装并注册 Pinia
  • 定义 store(state / getter / action)
  • 在组件中读写 store
  • 理解 Pinia 相比 Vuex 的简化点

一、安装 ​

bash
npm i pinia
ts
// src/main.ts
import { createPinia } from 'pinia'
app.use(createPinia())

二、定义 Store(选项式) ​

ts
// src/stores/user.ts
import { defineStore } from 'pinia'

export const useUserStore = defineStore('user', {
  state: () => ({
    name: 'Alice',
    age: 18,
    token: '',
  }),
  getters: {
    isAdult: (state) => state.age >= 18,
    doubleAge(): number {
      return this.age * 2     // getters 可以互相访问
    },
  },
  actions: {
    async login(username: string, password: string) {
      const res = await api.login({ username, password })
      this.token = res.data.token
      this.name = username
    },
    logout() {
      this.token = ''
    },
  },
})

三、Setup 写法(推荐,更像组合式) ​

ts
// src/stores/cart.ts
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'

export const useCartStore = defineStore('cart', () => {
  const items = ref<{ id: number; name: string; qty: number }[]>([])
  const totalQty = computed(() => items.value.reduce((s, i) => s + i.qty, 0))

  const add = (item: { id: number; name: string }) => {
    const exist = items.value.find(i => i.id === item.id)
    if (exist) exist.qty++
    else items.value.push({ ...item, qty: 1 })
  }
  const remove = (id: number) => {
    items.value = items.value.filter(i => i.id !== id)
  }

  return { items, totalQty, add, remove }
})

⚠️ 坑 1:Setup 写法里 ref 暴露出去时函数名会被当作 key,rename 后访问不到。

四、组件中使用 ​

vue
<script setup lang="ts">
import { useUserStore } from '@/stores/user'
import { storeToRefs } from 'pinia'

const store = useUserStore()
const { name, isAdult } = storeToRefs(store)   // 解构 state/getter 保留响应性
const { login } = store                         // action 直接解构
</script>

<template>
  <p>{{ name }} - {{ isAdult ? '成年' : '未成年' }}</p>
  <button @click="login('alice', '123')">登录</button>
</template>

⚠️ 坑 2:直接 const { name } = store 会丢失响应性,必须用 storeToRefs。 ⚠️ 坑 3:state 里不要直接 state: () => ({ token: localStorage.getItem('t') }),SSR/刷新会出错,改用 plugins(第 12 章)。

五、修改 State 的三种方式 ​

ts
// 1. 直接改
store.name = 'Bob'

// 2. $patch(批量)
store.$patch({ name: 'Bob', age: 20 })

// 3. $patch(函数形式,适合数组)
store.$patch((state) => {
  state.items.push({ id: 1, name: 'x', qty: 1 })
})

// 4. 整体替换
store.$state = { name: 'C', age: 30, token: '' }

六、订阅 store ​

ts
const unsubscribe = store.$subscribe((mutation, state) => {
  console.log(mutation.type, mutation.payload)
  localStorage.setItem('cart', JSON.stringify(state.items))
})

// 组件卸载时
onUnmounted(() => unsubscribe())

七、本章小结 ​

概念关键
defineStore定义一个 store
Setup 写法像 useXxx() 一样组织
storeToRefs解构保留响应性
$patch批量更新
$subscribe监听 state 变化

动手练习 ​

  1. 用户 store:state { name, token },action login / logout,getter isLogin
  2. 购物车 store:setup 写法,实现 add / remove / totalQty
  3. 持久化:用 $subscribe 把 token 写入 localStorage

下一章:第 12 章:Pinia 进阶 →

本站基于 VitePress 构建 · 由 StackHub 团队维护