pinia

103 阅读3分钟

定义Store

defineStore("自定义名字", {}) || defineStore("自定义名字", ()=>{}) 定义Store,它的第一个参数要求是一个独一无二的名字,第二个参数是其他配置 (选项式 | 组合式)

import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', { 
    // 其他配置 (选项式 | 组合式)... 
})

使用Store

创建组合式 Store

export const useCounterStore = defineStore('counter', () => { 
    const count = ref(0) 
    const doubleCount = computed(() => count.value * 2) 
    function increment() { count.value++ } 
    return { count, doubleCount, increment } 
})

组合式使用 Store

<script setup> 
    import { storeToRefs } from 'pinia'
    import { useCounterStore } from '@/stores/counter'
    const store = useCounterStore() 
    // 解构 store 中 state、 getters 只能使用 storeToRefs()。 另外 actions 可以直接结构使用
    const { count, doubleCount } = storeToRefs(store)
    const { increment } = store
</script>

创建选项式 Store

export const useCounterStore = defineStore('counter', {
    state: (): State => ({
      count: 66,
      name: "hello",
      getAddAge: "haihaihai"
    }),
    getters: {
        increment: (state) => state.count * 2,
        getNameAndAge(): string {
          return this.name + this.getAddAge;
        }
    }, 
    actions: {
        doubleCount() {
          return this.count++
        }
    }

选项式使用 Store

<script setup> 
    import { storeToRefs } from 'pinia'
    import { useCounterStore } from '@/stores/counter'
    export default { 
        name: 'PiniaDemoPage1', 
        setup () { 
            const counterStore = useCounterStore() 
            return { counterStore } 
        },
        methods: {
            fetchData() {
              this.store.doubleCount()
            }
        }
    }
</script>
<template>
    <h1>increment:{{ store.increment }}</h1>
    <button @click="fetchData">Fetch Data{{ store.count }}</button>
    <h1>This is an home page {{ store?.name }}</h1>
</template>

使用Store并解构

在解构 stategetters 需要使用 pinia 中 storeToRefs来进行解构,否则直接结构会破坏了响应性,只有actions可以直接解构,如下:

<script setup> 
    import { storeToRefs } from 'pinia' 
    const store = useCounterStore() 

    // `name` 和 `doubleCount` 是响应式的 ref 
    // 同时通过插件添加的属性也会被提取为 ref 
    // 并且会跳过所有的 action 或非响应式 (不是 ref 或 reactive) 的属性 
    const { name, doubleCount } = storeToRefs(store) 

    // 作为 action 的 increment 可以直接解构 
    const { increment } = store 
</script>

Setup Store 中

  • ref() 就是 state 
  • computed() 就是 getters
  • function() 就是 actions

state

第一. 重置 state $reset()

  • 组合式: 您需要创建自己的 $reset() 方法

    export const useCounterStore = defineStore('counter', () => { 
        const count = ref(0) 
        function $reset() { 
            count.value = 0 
        } 
        return { count, $reset } 
    })
    
  • 选项式:在 $reset() 内部,会调用 state() 函数来创建一个新的状态对象,并用它替换当前状态

    const store = useStore()
    // 重置
    store.$reset()
    

第二. 变更 state $patch (选项式和组合式都可以这样处理)

store.$patch({ 
    count: store.count + 1, 
    age: 120, 
    name: 'DIO', 
})
store.$patch((state) => { 
    state.items.push({ name: 'shoes', quantity: 1 }) 
    state.hasChanged = true 
})

第三. 替换 state $state

// 这实际上并没有替换
`$state` store.$state = { count: 24 } 
// 在它内部调用 
`$patch()`: store.$patch({ count: 24 })

第四. 订阅 state $subscribe

counterStore.$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 整个数据
  console.log("state", state)
})

第二个参数是 Boolean, 当为 true 时候表示就算组件卸载也会一直监听。如果参数是 false 时候表示组件卸载,监听也会取消监听。

counterStore.$subscribe((mutation, state) => {}, true)

counterStore.$subscribe((mutation, state) => {}, false)

  • 组合式
<script setup> 
    const counterStore = useCounterStore() 
    // 订阅器
    counterStore.$subscribe(callback, boolean) 
</script>
  • 选项式:放在 setup() 中
  setup() {
    const counterStore = useCounterStore()
    // 订阅器
    counterStore.$subscribe(callback, boolean)
    return { store }
  },

getter

第一. getter 相当于组件中的 computed

第二. 在选项式中是 getter, 在组合式中是 computed, 例子请看上面 使用Store

第三. 其拥有缓存能力,当其内部所依赖的state没变化不会在执行

第四. 向 getter 传递参数,注意: 会导致其将不再被缓存

export const useUserListStore = defineStore('userList', {
  getters: {
    getUserById: (state) => {
      return (userId) => state.users.find((user) => user.id === userId) // 不在拥有缓存能力
    },
  },
})

第五. 访问其他 store 的 getter

import { useOtherStore } from './other-store'

export const useStore = defineStore('main', {
  state: () => ({
    // ...
  }),
  getters: {
    otherGetter(state) {
      const otherStore = useOtherStore()
      return state.localData + otherStore.data
    },
  },
})

action

第一. 在选项式中是 actions, 在组合式中是 function, 例子请看上面 使用Store

第二. actions 可以是异步的

import { mande } from 'mande'

const api = mande('/api/users')

export const useUsers = defineStore('users', {
  state: () => ({
    userData: null,
    // ...
  }),

  actions: {
    async registerUser(login, password) {
      try {
        this.userData = await api.post({ login, password })
        showTooltip(`Welcome back ${this.userData.name}!`)
      } catch (error) {
        showTooltip(error)
        // 让表单组件显示错误
        return error
      }
    },
  },
})

第三. 订阅 action $onAction

第二个参数是 Boolean, 当为 true 时候表示就算组件卸载也会一直监听。如果参数是 false 时候表示组件卸载,监听也会取消监听。

someStore.$onAction(callback, Boolean)

  • 组合式
<script setup> 
    const counterStore = useCounterStore() 
    // 订阅器
    counterStore.$onAction(callback, boolean) 
</script>
  • 选项式:放在 setup() 中
  setup() {
    const counterStore = useCounterStore()
    // 订阅器
    counterStore.$onAction(callback, boolean)
    return { store }
  },

TypeSctipt

interface State { 
    userList: UserInfo[] 
    user: UserInfo | null 
} 
const useStore = defineStore('storeId', { 
    state: (): State => { 
        return { 
            userList: [], 
            user: null, 
        } 
    }
})