state - Pinia(状态管理)
state
在大多数情况下,state 都是你的 store 的核心。人们通常会先定义能代表他们 APP 的 state。在 Pinia 中,state 被定义为一个返回初始状态的函数。这使得 Pinia 可以同时支持服务端和客户端。
import { defineStore } from 'pinia' export const useStore = defineStore('storeId', { // 为了完整类型推理,推荐使用箭头函数 state: () => { return { // 所有这些属性都将自动推断出它们的类型 count: 0, name: 'Eduardo', isAdmin: true, } }, })
如果你使用的是 Vue 2,你在 state 中创建的数据与 Vue 实例中的data
遵循同样的规则,即 state 对象必须是清晰的,当你想向其添加新属性时,你需要调用Vue.set()
时调用。另请参阅: Vue#data。
state 被定义为返回初始状态的函数,推荐使用箭头函数。箭头函数返回的必须是对象。有两种书写方式,其效果是等同:
第一种:{return{}}
import { defineStore } from 'pinia' export const useStore = defineStore('storeId', { state: () => { return { count: 0, name: 'Eduardo', isAdmin: true, } }, })
第二种:({})
import { defineStore } from 'pinia' export const useStore = defineStore('storeId', { state: () => ({ count: 0, name: 'Eduardo', isAdmin: true, }), })
TypeScript
你并不需要做太多努力就能使你的 state 兼容 TS。只需要strict
或至少noImplicitThis
启用。Pinia 将自动推断您的 state 类型!但在一些情况下,你得用一些方法来帮它一把。
export const useUserStore = defineStore('user', { state: () => { return { // 用于初始化空列表 userList: [] as UserInfo[], // 用于尚未加载的数据 user: null as UserInfo | null, } }, }) interface UserInfo { name: string age: number }
如果您愿意,可以使用接口定义 state,并添加state()
的返回值的类型。
interface State { userList: UserInfo[] user: UserInfo | null } export const useUserStore = defineStore('user', { state: (): State => { return { userList: [], user: null, } }, }) interface UserInfo { name: string age: number }
访问 state
默认情况下,你可以通过 store 实例访问 state,直接对其进行读写。
const store = useStore() store.count++
重置 state
你可以通过调用 store 的$reset()
方法将 state 重置为初始值。
const store = useStore() store.$reset()
使用选项式 API
对于以下示例,您可以假设已创建以下 Store:
// Example File Path: // ./src/stores/counter.js import { defineStore } from 'pinia' export const useCounterStore = defineStore('counter', { state: () => ({ count: 0, }), })
如果你不能使用 Composition API(组合式 API),但你可以使用computed
、methods
等等,则可以使用mapState()辅助函数,将 state 属性映射为只读的计算属性:
import { mapState } from 'pinia' import { useCounterStore } from '../stores/counter' export default { computed: { // 可以访问组件中的 this.count // 与从 store.count 中读取的数据相同 ...mapState(useCounterStore, ['count']) // 与上述相同,但将其注册为 this.myOwnName ...mapState(useCounterStore, { myOwnName: 'count', // 你也可以写一个函数来获得对 store 的访问权 double: store => store.count * 2, // 它可以访问 `this`,但它没有标注类型... magicValue(store) { return store.someGetter + this.count + this.double }, }), }, }
可修改state
如果您希望能够修改这些 state 状态属性(例如,如果您有一个表单),您可以mapWritableState()。但不能像使用mapState
那样传递函数:
import { mapWritableState } from 'pinia' import { useCounterStore } from '../stores/counter' export default { computed: { // 可以访问组件中的 this.count,并允许设置它。 // this.count++ // 与从 store.count 中读取的数据相同 ...mapWritableState(useCounterStore, ['count']) // 与上述相同,但将其注册为 this.myOwnName ...mapWritableState(useCounterStore, { myOwnName: 'count', }), }, }
对于像数组这样的集合,你并不一定需要使用mapWritableState()
,也可以使用mapState()
来调用集合上的方法,除非你想用cartItems =[]
替换整个数组。
变更 state
除了用store.count++
直接变更 store 之外。您还可以调用$patch方法。它允许您对 state 对象,同时更改多个属性:
store.$patch({ count: store.count + 1, age: 120, name: 'DIO', })
但是,使用这种语法变更,很难实现或者很耗时:任何集合修改(例如,从数组中推送、删除、拼接元素)都需要您创建一个新集合。正因为如此,该$patch
方法还接受一个函数,来组合这种难以用补丁对象实现的变更。
cartStore.$patch((state) => { state.items.push({ name: 'shoes', quantity: 1 }) state.hasChanged = true })
两种变更 store 方法的主要区别是,$patch()
允许你将多个变更归入 devtools 的同一个条目中。同时请注意,直接修改 state,$patch()
也会出现在 devtools 中,而且可以进行 time travel(在 Vue 3 中还没有)。
替换 state
你不能完全替换掉 store 的 state,因为那样会破坏其响应性。但是,你可以 patch 它。
// 这实际上并没有替换`$state` store.$state = { count: 24 } // 在它内部调用 `$patch()`: store.$patch({ count: 24 })
你也可以通过变更 pinia 实例的 state 来设置整个应用的初始 state。这常用于 SSR 中的激活过程
pinia.state.value = {}
订阅 state
你可以通过 store 的$subscribe()方法侦听 state 及其变化,类似于 Vuex 的subscribe
方法。$subscribe()
与常规watch()
相比使用的优点是:subscriptions 在 patch 后只触发一次(例如,使用上面的函数版本时)。
const store = useStore(); const subscribe = store.$subscribe( (mutation, state) => { // 我们就可以在此处监听 store 中值的变化,当变化为某个值的时候,去做一些业务操作之类的 console.log(mutation) console.log(state) }, {detached: false} ); // 停止订阅。调用上方声明的变量值,示例(subscribe),即可以停止订阅 subscribe();
$subscribe第一个参数是箭头函数,其参数mutation:检测 store 值的改变,包含三个属性值:
- events:当前state改变的具体数据,包括改变前的值和改变后的值等等数据
- storeId:是当前 store 的 id
- type:用于记录这次数据变化是通过什么途径,主要有三个分别是
direct
:通过 action 变化的patch object
:通过$patch 传递对象的方式改变的patch function
:通过$patch 传递函数的方式改变的
$subscribe第二个参数是options对象,是各种配置参数,和 vue3 watch 的参数是一样的。
- detached:布尔值,默认是 false,正常情况下,当订阅所在的组件被卸载时,订阅将被停止删除,如果设置 detached 值为 true 时,即使所在组件被卸载,订阅依然在生效。
- deep:如果源是对象,则强制深度遍历源,以便回调触发深度突变。请参阅深度观察者。
- immediate:在观察者创建时立即触发回调。旧值将 undefined 在第一次调用时出现。
- flush:调整回调的刷新时间。
cartStore.$subscribe((mutation, state) => { // import { MutationType } from 'pinia' mutation.type // 'direct' | 'patch object' | 'patch function' // same as cartStore.$id mutation.storeId // 'cart' // only available with mutation.type === 'patch object' mutation.payload // patch object passed to cartStore.$patch() // persist the whole state to the local storage whenever it changes localStorage.setItem('cart', JSON.stringify(state)) })
默认情况下,state subscription 会被绑定到添加它们的组件上(如果 store 在组件的setup()
里面)。这意味着,当该组件被卸载时,它们将被自动删除。如果你想在组件卸载后依旧保留它们,请将{detached: true}
作为第二个参数,以将 state subscription 从当前组件中分离:
export default { setup() { const someStore = useSomeStore() // 在组件被卸载后,该订阅依旧会被保留 someStore.$subscribe(callback, { detached: true }) // ... }, }
你可以在 pinia 实例上侦听整个 state。
watch( pinia.state, (state) => { // 每当状态发生变化时,将整个 state 持久化到本地存储 localStorage.setItem('piniaState', JSON.stringify(state)) }, { deep: true } )
鹏仔微信 15129739599 鹏仔QQ344225443 鹏仔前端 pjxi.com 共享博客 sharedbk.com
图片声明:本站部分配图来自网络。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!