• 首页
  • vue
  • TypeScript
  • JavaScript
  • scss
  • css3
  • html5
  • php
  • MySQL
  • redis
  • jQuery
  • 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),但你可以使用computedmethods等等,则可以使用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 }
    )
    

    上篇:defineStore

    下篇:getters