pinia
Pinia 如何定义
import {defineStore} from 'pinia'
export const useAlertStore = defineStore('alters_id', {
})
// alters_id: 当前 useAlertStore 的ID,必须唯一
file: alterStore.ts
Pinia 用 alters_id 来链接store 和 devtools。建议的命名规则 use…Store, defineStore() 的第二个参数可以接受两类值:Setup 函数或 Option对象
Option Store
与Vue 的选项式API 类似, 我们也可以传入一个带有state、actions 与 getters 属性的Option对象
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0
}),
getters: {
double: (state) => state.count * 2
},
actions: {
increment() {
this.count++
}
}
})
- state 就是 store 中的数据data
- getters 就是store 中的计算属性 computed
- actions 就是 方法 methods
Setup Store
另外一种定义store 的可用语法。与Vue组合式API 的 setup 函数相似,我们也可以传入一个函数,该函数定义了一些响应式属性和方法,并且返回一个带有我们想要暴露出去的属性和方法的对象。
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
function increment() {
count.value++
}
return {count, increment}
})
在Setup store 中:
- ref() 就是 state
- computed() 就是 getters
- function() 就是 actions
Setup store 比 Option Store 带来了更多的灵活性,因为可以在一个store 内创建侦听器,并自由的使用任何组合式函数。
使用Store
虽然我们前面定义了一个store,但我们调用 useStore() 之前,store 实例是不会被创建的:
<script setup>
import { useCounterStore } from '@/store/counter'
const store = useCounterStore()
</script>
一旦store 被实例化,你可以直接访问store的 state、getters和actions 中定义的任何属性。请注意,store是一个用reactive 包装的对象,这意味着不需要getters后面写.value, 就像setup中的props一样
为了从store中提取属时保持其响应性,需要使用storeToRefs()。它将为每一个响应式属性创建引用。请注意,可以直接从store中解构action,因为他们也被绑定到store 上:
<script setup>
import { storeToRefs } from 'pinia'
const store = useCounterStore()
const {name, doubleCount} = storeToRefs(store)
const {increment} = store
</script>
State
在大多数情况下,state 都是 store 的核心。人们通常会先定义能代表app 的state。在pinia中,state被定义为一个返回初始状态的函数。
import {defineStore} from 'pinia'
const useStore = define('storeId', {
state: () => {
return {
count: 0,
name: 'Eduardo',
isAdmin: false,
items: [],
hasChanged:true,
}
},
})
如果你愿意,可以用一个接口定义state,并添加 state() 的返回值的类型。
interface State {
userList: UserInfo[]
user: UserInfo | null
}
interface UserInfo {
name: string
age: number
}
const useStore = defineStore('storeId', {
state: ():State => {
return {
userList: [],
user:null,
}
}
})
访问state
默认情况下,你可以通过 store 实例访问state,直接对其进行读写。
const store = useStore()
store.count++
重置 state
使用选项式API 时,可以通过调用store 的 $reset() 方法将 state 重置为初始值。
const store = useStore()
store.$reset()
变更state
除了直接用 store.count++ 直接变更store,还可以调用 $patch方法。它允许你用一个 state的补丁对象在同一时间更改多个属性:
store.$patch({
count: store.count+1,
age:120,
name:'DIO',
})
有些变更很难实现或很耗时,比如集合的修改都需要创建一个新的集合,因此$patch 方法也接受一个函数来实现数据变更:
store.$patch((state) => {
state.items.push({name:'shoes', quantity:1})
state.hasChanged = true
})
替换State
不能完全替换掉store 的 state,因为那样会破坏其响应性。但是,可以通过patch它,来实现内容的全部修改。
store.$state = {count:23}
stare.$patch({count:22})
订阅 state
类似于Vuex 的 subscribe 方法, 你可以通过store 的 $subscribe() 方法侦听state及其变化,比起普通的 watch(), 使用 $subscribe() 的好处是 subscriptions 在 patch后只触发一次。
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>
可以在 pinia 实例上使用 watch() 函数侦听整个 state。
watch(
pinia.state,
(state) => {
// 每当状态发生变化时,将整个 state 持久化到本地存储。
localStorage.setItem('piniaState', JSON.stringify(state))
},
{ deep: true }
)
Getter
Getter 完全等同于store 的state 的计算值。可以通过 defineStore() 中的 getters 属性来定义他们。建议使用箭头函数,并且它将接收state 作为第一个参数:
export const useStore = defineStore('main', {
state:() => ({
count: 0
}),
getters: {
doubleCount: (state) => state.count * 2,
},
})
大多数时候,getter仅依赖于state,不过,有时它们也会使用其他getter。因此即使使用常规函数定义getter时,我们也可以通过this 访问到整个store实例,但在ts中,必须定义返回类型。这是避免ts的已知缺陷,不过这不影响用箭头函数定义的getter,也不会影响使用this的getter。
export const useStore = defineStore('main', {
state: () => {
count:0,
},
getters: {
doubleCount(state) {
return state.count * 2
},
doublePlusOne() {
return this.doubleCount + 1
},
},
})
然后你可以访问store实例上的getter了:
<script setup>
import { useCounterStore } from './counterStore'
const store = useCounterStore()
</script>
<template>
<p>Double count is </p>
</template>
向getter 传递参数
Getter 只是幕后的计算属性,所以不可以向他们传递任何参数。不过可以从getter返回一个函数,该函数可以接受任意参数:
export const useStore = defineStore('main', {
getters:{
getUserById: (state) => {
return (userId) => state.users.find((user) => user.id === userId)
},
},
})