这一节了解一下Vue3中的状态管理,在Vue中,Pinia是官方推荐的状态管理库,它是一个专为Vue设计的轻量级、类型安全的状态管理库。它允许开发者跨组件、跨页面共享和管理全局状态,简单总结如下:
API:
1 createPinia():创建Pinia根实例,类似Vuex的Store根,注册到Vue app中
2 defineStore(id, options)定义仓库store,id是仓库唯一标识;定义state、getters、actions
3 state 状态:存储共享响应式数据(相当于组件`data()`)
4 getters 计算属性:基于 state 派生数据,缓存结果,类似组件computed
5 actions 方法:修改state的函数,支持同步、异步请求;Pinia没有mutation,直接改state或在actions修改
6 state 直接替换整个store状态对象
7 reset() 将state重置回初始默认值
8 patch() 批量修改 state,可以传对象/函数,减少多次响应式更新
9 subscribe() 订阅state变化,state 改动自动触发回调
10 storeToRefs() 从store解构数据保持响应式;直接解构会丢失响应,必须用该API
场景:
1 跨组件/跨页面共享状态
2 需要状态持久化的应用
3 TypeScript项目
栗子:
javascript
main.js注 pinia
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const app = createApp(App)
const pinia = createPinia()
app.use(pinia)
app.mount('#app')
javascript
stores/counter.js定义store
import { defineStore } from 'pinia'
// id:"counter" 唯一仓库标识
export const useCounterStore = defineStore('counter', {
// state:共享状态
state() {
return {
count: 0,
userName: "张三"
}
},
getters: {
doubleCount(state) {
return state.count * 2
}
},
actions: {
increment() {
// 直接修改state,不需要mutation
this.count++
},
decrement() {
this.count--
},
resetCount() {
this.$reset() //重置state到初始值
}
}
})
javascript
App.vue使用store
<template>
<div>
<h3>Pinia计数器Demo</h3>
<p>count:{{ counterStore.count }}</p>
<p>doubleCount(getter):{{ counterStore.doubleCount }}</p>
<p>用户名:{{ counterStore.userName }}</p>
<button @click="counterStore.increment">+1</button>
<button @click="counterStore.decrement">-1</button>
<button @click="counterStore.resetCount">重置</button>
<h4>storeToRefs解构</h4>
<p>解构count: {{ count }}</p>
<button @click="count++">直接修改解构后的count</button>
</div>
</template>
<script setup>
import { useCounterStore } from './stores/counter'
import { storeToRefs } from 'pinia'
const counterStore = useCounterStore()
const { count } = storeToRefs(counterStore)
</script>
Demo2
javascript
stores/user.js
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
state() {
return {
token: "",
isLogin: false,
userId: null,
nickName: ""
}
},
getters: {
isTokenValid(state) {
return !!state.token
}
},
actions: {
async login(username, password) {
await new Promise(resolve => setTimeout(resolve, 800))
if(username === "admin" && password === "123456"){
this.$patch({
token: "mock-token-0001",
isLogin: true,
userId: 1001,
nickName: "管理员"
})
}else{
throw new Error("账号密码错误")
}
},
logout() {
this.$reset()
}
}
})
javascript
App.vue
<template>
<div>
<h3>Pinia异步登录Demo</h3>
<div>
<input v-model="username" placeholder="用户名admin" />
<input v-model="password" placeholder="密码123456" type="password"/>
<button @click="handleLogin" :disabled="loading">登录</button>
<button @click="userStore.logout">退出登录</button>
</div>
<div v-if="userStore.isLogin">
<p>登录成功!</p>
<p>nickName:{{ userStore.nickName }}</p>
<p>token:{{ userStore.token }}</p>
<p>token有效(getter): {{ userStore.isTokenValid }}</p>
</div>
<p v-if="errMsg" style="color:red">{{ errMsg }}</p>
</div>
</template>
<script setup>
import { ref } from "vue"
import { useUserStore } from "./stores/user"
const userStore = useUserStore()
const username = ref("")
const password = ref("")
const loading = ref(false)
const errMsg = ref("")
userStore.$subscribe((mutation, state) => {
console.log("state发生变更:", state)
})
const handleLogin = async ()=>{
loading.value = true
errMsg.value = ""
try {
await userStore.login(username.value, password.value)
} catch(e) {
errMsg.value = e.message
} finally {
loading.value = false
}
}
</script>
注意:
1 解构必须使用storeToRefs
2 Store之间避免循环依赖