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)
            },
        },
    })