百科狗-知识改变命运!
--

state - Pinia(状态管理)

梵高1年前 (2023-11-21)阅读数 22#技术干货
文章标签函数

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 被定义为返回初始状态的函数,推荐使用箭头函数。箭头函数返回的必须是对象。有两种书写方式,其效果是等同

state - Pinia(状态管理)

第一种:{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 }
)

鹏仔微信 15129739599 鹏仔QQ344225443 鹏仔前端 pjxi.com 共享博客 sharedbk.com

免责声明:我们致力于保护作者版权,注重分享,当前被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理!邮箱:344225443@qq.com)

图片声明:本站部分配图来自网络。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

内容声明:本文中引用的各种信息及资料(包括但不限于文字、数据、图表及超链接等)均来源于该信息及资料的相关主体(包括但不限于公司、媒体、协会等机构)的官方网站或公开发表的信息。部分内容参考包括:(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供参考使用,不准确地方联系删除处理!本站为非盈利性质站点,本着为中国教育事业出一份力,发布内容不收取任何费用也不接任何广告!)