第 11 章:Pinia 状态管理
学习目标
- 安装并注册 Pinia
- 定义 store(state / getter / action)
- 在组件中读写 store
- 理解 Pinia 相比 Vuex 的简化点
一、安装
bash
npm i piniats
// 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 变化 |
动手练习
- 用户 store:state
{ name, token },actionlogin / logout,getterisLogin - 购物车 store:setup 写法,实现 add / remove / totalQty
- 持久化:用
$subscribe把 token 写入localStorage
下一章:第 12 章:Pinia 进阶 →