一,前言
1.pinia
和vuex
一样是vue
的状态管理器,从vue2
升级到vue3
加入了组合式API
的概念后,vue
已经主推pinia
作为状态管理器
2.与 Vuex
相比,Pinia
提供了一个更简单的 API
,也提供了符合组合式 API
风格的 API
,最重要的是,搭配 TypeScript
一起使用时有非常可靠的类型推断支持
二,相对于vuex
的优点
1.抛弃了Mutations
的操作,简化了代码,本身比vuex
更轻量级
2.抛弃了module
,没有嵌套,使代码更扁平化
3.有完整的TypeScript
支持
三,安装
1.使用任意包管理工具安装pinia
npm install pinia
四,初始化+注册
1.和vuex
一样,需要初始化一个pinia
实例(store
实例),注册到vue
中,
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import app from "./App.vue"
const pinia = createPinia() //无需入口文件,扁平化
const app = createApp(App)
app.use(pinia )
2.注意,不同于vuex
,初始化时无需入口文件,只要调用createPinia
之后即可,之后定义的store
,可以任意使用,整体更扁平化
3.同vuex
,pinia
在全局中也只有一个store
实例,在组件外使用时useXxxStore
时,需考虑要在实例已经注册之后,在组件内部使用时一定是注册后的,可放心使用
五,store
介绍
1.一个完整的store
包含3个配置项
(1)状态:state
(2)计算属性:getters
(3)异步修改:actions
(4)同步修改:mutations 弃用
(5)模块化:modules 弃用
六,定义store
1.要定义一个store
,需要使用pinia
提供的API
: defineStore
2.defineStore
第一个参数需要是独一无二的值作为该store
的id
和命名,这也反映了在pinia
中每个store
都需要命名,
3.defineStore
第二个参数可以是OptionAPI
或Setup
函数,定义store
的state
,action
和getters
(1)Option API
的定义方式基本上和vuex
一样,
export const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 }),
getters: {
double: (state) => state.count * 2,
},
actions: {
increment() {
this.count++
},
},
})
(2)Setup
函数的使用方式整体像使用hook
,使用组合式api
,使用ref
等定义变量
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
function increment() {
count.value++
}
return { count, increment }
})
七,state
1.定义state
const useStore = defineStore("storeId", {
//为了完整类型推理,推荐使用箭头函数
state:()=>{
// 所有这些属性都将自动推断出它们的类型
userName:"",
userId:"",
}
})
2.访问state
const store = useStore()
store.userName
3.修改state
(1)访问时修改
const store = useStore()
store.userName = 'abc'
(2)使用$patch
,可支持同一时间更改多个属性
const store = useStore()
store.$patch({
userName: store.userName + "d",
userId: '123',
})
//当是复杂类型数据的push,splice等时,用上面的方面需要创建一个新的集合,可以使用下面方法
store.$patch((state) => {
state.items.push({ name: 'shoes', quantity: 1 })
})
4.重置state
,注意重置整个state
为初始值
const store = useStore()
store.$reset()
5.订阅state
,当state
改变时,调用callback
函数,参数如下
cartStore.$subscribe((mutation, state) => {
// import { MutationType } from 'pinia'
mutation.type // 'direct' | 'patch object' | 'patch function'
// 和 cartStore.$id 一样
mutation.storeId // 'cart'
// 只有 mutation.type === 'patch object'的情况下才可用
mutation.payload // 传递给 cartStore.$patch() 的补丁对象。
// 每当状态发生变化时,将整个 state 持久化到本地存储。
localStorage.setItem('cart', JSON.stringify(state))
})
默认情况下,state subscription
会被绑定到添加它们的组件上 (如果 store
在组件的 setup()
里面)。这意味着,当该组件被卸载时,它们将被自动删除。如果你想在组件卸载后依旧保留它们,可将 { detached: true }
作为第二个参数,以将 state subscription
从当前组件中分离
<script setup>
const someStore = useSomeStore()
// 此订阅器即便在组件卸载之后仍会被保留
someStore.$subscribe(callback, { detached: true })
</script>
八,getter
1.定义getter
,使用箭头函数定义,第一个参数是state
const useStore = defineStore("storeId", {
state:()=>{
userName:"",
userId:"",
},
getters:{
bigName:(state) => state.userName + 'big'
}
})
2.你可以在getter
里使用this
访问整个store
实例 ,因此我们可以在getter
里访问其他getter
const store = defineStore("storeId", {
state:()=>{
userName:"",
userId:"",
},
getters:{
bigName:(state) => state.userName + 'big',
bigName2:(state) => state.userName + this.bigName,
}
})
3.我们可以像访问state
一样,访问getter
<div>{{ store.bigName }}</div>
4.在访问getter
时,我们可以向getter
传递参数,注意,本身getter
是不支持传参的,我们可以通过返回一个函数,该函数可以接受任意参数
const store = defineStore("storeId", {
state: () => {
userName: "",
userId: "",
},
getters: {
getUserById: (state) => {
return (userId) => state.users.find((user) => user.id === userId)
},
}
})
九,action
1.同vuex
,我们在action
里定义方法,同时支持同步或异步方法,可使用async
函数
const store = defineStore("storeId", {
state: () => {
userName: "",
userId: "",
},
actions:{
increment() {
this.userName = 123
},
async fn(){
await ....
}
}
})
2.我们可以在actions
函数中,使用this
访问整个store
实例,要访问自身store
的state
,只要使用this.stateProp
就行,要访问其他store
,在函数中引入这个store
即可
3.使用actions
里的函数,也摈弃了vuex
的dispatch
方法,只要像调用一个函数一样调用actions
里的函数就可以了
<script setup>
const store = useCounterStore()
// 将 action 作为 store 的方法进行调用
store.randomizeCounter()
</script>
<template>
<!-- 即使在模板中也可以 -->
<button @click="store.randomizeCounter()">Randomize</button>
</template>