vue3中pinia详解
基础概念:
Pinia 是 Vue.js 的轻量级状态管理库,允许跨组件/页面共享状态,被视为 Vuex 的替代品。
简单运用:
安装:
npm install pinia
# 或
yarn add pinia
入口文件(main.js):
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'const pinia = createPinia()
const app = createApp(App)app.use(pinia)
app.mount('#app')
Store:
- store文件名为counter.js ,文件命名要尽量简单而且知文达意。
- Store 使用 defineStore() 定义,它需要一个id(唯一性),作为第一个参数传递,是为了方便使用浏览器插件vue devtools。
- 将返回的函数命名为 useCounterStore ,这种 use... 的命名方式是前端程序员之间的通俗约定。
import { defineStore } from 'pinia'export const useCounterStore = defineStore('counter', {
//counter就是这个Store的idstate: () => ({ count: 0 }),getters: {double: (state) => state.count * 2,},actions: {increment() {this.count++},},
})
组件中使用:
import { useCounterStore } from '@/stores/counter'const counter = useCounterStore()// 直接访问 state
count: computed(() => counter.count),
console.log(counter.count)// 0
// 访问 getter
doubleCount: computed(() => counter.doubleCount)
console.log(counter.doubleCount)// 0
三个核心:
Pinia 的核心单元,包含状态(state)、getters 和 actions,他们可以简单理解为vue2中的data,computed,methods。
state:
state 是存储应用数据的地方
定义 state:
import { defineStore } from 'pinia'export const useCounterStore = defineStore('counter', {state: () => ({count: 0,name: 'John Doe',items: [],isActive: true})
})
访问state:
import { useCounterStore } from '@/stores/counter'const store = useCounterStore()// 直接访问
console.log(store.count)// 0
- store 被实例化后,可以直接访问 store 上的state、getters 和 actions
- store 是 reactive 对象,所以在使用 getters 中的属性时,不用在getter 之后写.value。
- 对 store 进行解构时,直接解构是会失去响应性的,需要使用 storeToRefs 来保持响应性
import { storeToRefs } from 'pinia'const counter = useCounterStore()
const { count, double } = storeToRefs(counter)
修改 state :
直接修改:
store.count++
store.name = 'Jane Doe'
使用 $patch 方法批量修改:
store.$patch({count: store.count + 1,name: 'Jane Doe'
})// 或者使用函数形式
store.$patch((state) => {state.items.push({ name: 'item' })state.isActive = false
})
使用 $state 替换整个 state:
store.$state = { count: 999, name: 'Paimon' }
重置 state 为初始值:
store.$reset()
订阅(监听) State 变化:
import { useStore } from './store'const store = useStore()// 订阅状态变化
const userSubscribe = store.$subscribe((mutation, state) => {// mutation 包含变更信息console.log('变更类型:', mutation.type)console.log('变更的 store id:', mutation.storeId)console.log('变更的 payload:', mutation.payload)// state 是变更后的完整状态console.log('新状态:', state)
})// 取消订阅
unsubscribe()
$subscribe 回调函数接收两个参数:
mutation 对象包含:
- type: 变更类型 ('direct' | 'patch object' | 'patch function')
- storeId: 发生变更的 store id
- payload: 传递给 $patch 的 payload(如果是直接修改则不存在)
state: 变更后的完整 store 状态
$subscribe 还接受一个可选的 options 对象作为第二个参数:
store.$subscribe((mutation, state) => {// 回调逻辑
}, {detached: true, // 当组件卸载时保持订阅(默认为 false)deep: true, // 深度监听(默认为 false)flush: 'post' // 回调触发时机:'pre' | 'post' | 'sync'(默认为 'post')
})
与 watch 的区别:
- $subscribe 会响应所有状态变更(包括 $patch 和直接修改)
- $subscribe 提供变更的元信息(mutation 对象)
- watch 更适合监听特定状态属性的变化
getters:
Getters 用于从 store 中派生出状态的计算属性,类似于 Vue 中的计算属性或 Vuex 中的 getters。
定义 getters:
import { defineStore } from 'pinia'export const useStore = defineStore('main', {state: () => ({count: 0}),getters: {doubleCount: (state) => state.count * 2,// 可以访问其他 gettersdoubleCountPlusOne() {return this.doubleCount + 1 // this 指向 store 实例}}
})
使用 getters:
import { useStore } from '@/stores/main'const store = useStore()// 直接访问
let doubleCount = store.doubleCount,
// 或者作为计算属性
let doubleCountPlusOne = computed(() => store.doubleCountPlusOne)
特点:
- 类型推断:Pinia 会自动推断 getters 的类型
- this 访问:可以通过 this 访问整个 store 实例
- 传递参数:可以通过返回函数来接受参数
getters: {getUserById: (state) => {return (userId) => state.users.find((user) => user.id === userId)}
}
// 使用
store.getUserById(123)
- 访问其他 store:可以在 getter 中访问其他 store
import { useOtherStore } from './other-store'getters: {combinedValue() {const otherStore = useOtherStore()return this.count + otherStore.value}
}
与 Vuex 的区别:
- Pinia 的 getters 没有缓存的概念(Vuex 有缓存)
- Pinia 的 getters 只是计算属性,没有像 Vuex 那样的
mapGetters
辅助函数 - 在 Pinia 中,可以直接通过 store 实例访问 getters,不需要像 Vuex 那样通过
getters
对象
actions:
actions 是用来定义可以修改状态(state)的方法的地方。它可以包含业务逻辑,并且可以是异步的。
定义 actions:
import { defineStore } from 'pinia'export const useCounterStore = defineStore('counter', {state: () => ({count: 0}),actions: {increment() {this.count++},decrement() {this.count--}}
})
使用 actions:
const store = useCounterStore()
store.increment() // 调用 action
异步 actions:
actions: {async fetchUserData(userId) {// 拿取用户数据try {const response = await fetch(`/api/users/${userId}`)this.user = await response.json()} catch (error) {console.error('Failed to fetch user:', error)}}
}
访问其他 actions:
actions: {async login(userCredentials) {// 用户认证const user = await this.authenticate(userCredentials)this.setUser(user)// this 指向 store 实例},async authenticate(credentials) {// 认证逻辑},setUser(user) {this.user = user}
}
访问其他 Store:
actions: {async checkout() {const cartStore = useCartStore()const userStore = useUserStore()// 使用其他 store 的数据和方法if (!userStore.isLoggedIn) {await userStore.login()}await api.post('/checkout', {items: cartStore.items,userId: userStore.user.id})cartStore.clear()}
}
传递参数:
actions: {addTodo(text) {this.todos.push({ text, done: false })}
}
与 Getters 交互:
actions: {completeAll() {// 使用 getterthis.allTodos.forEach(todo => {todo.done = true})}
},
getters: {allTodos() {return this.todos}
}
Pinia 状态持久化:
Pinia 默认情况下状态不会持久化,页面刷新后状态会丢失。
官方推荐的持久化插件 pinia-plugin-persistedstate。
安装:
npm install pinia-plugin-persistedstate
# 或
yarn add pinia-plugin-persistedstate
基本使用:
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
store 中启用:
import { defineStore } from 'pinia'export const useStore = defineStore('storeId', {state: () => ({someState: 'hello pinia',}),persist: true, // 启用持久化
})
配置选项:
persist: {key: 'my-custom-key', // 存储的键名idstorage: localStorage, // 默认是 sessionStoragepaths: ['user', 'settings'], // 只持久化部分状态beforeRestore: (ctx) => { /* 恢复前 */ },afterRestore: (ctx) => { /* 恢复后 */ },
}