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的id state: () => ({ count: 0 }), getters: { double: (state) => state.count * 2, }, actions: { increment() { this.count++ }, },})
组件中使用:
import { useCounterStore } from \'@/stores/counter\'const counter = useCounterStore() // 直接访问 statecount: computed(() => counter.count),console.log(counter.count)// 0// 访问 getterdoubleCount: 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, // 可以访问其他 getters doubleCountPlusOne() { 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() { // 使用 getter this.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\', // 存储的键名id storage: localStorage, // 默认是 sessionStorage paths: [\'user\', \'settings\'], // 只持久化部分状态 beforeRestore: (ctx) => { /* 恢复前 */ }, afterRestore: (ctx) => { /* 恢复后 */ },}