简单学习一下 Pinia

引入 Pinia

首先在 main.ts 中引入 pinia,通过 createPinia 创建 pinia 实例,然后挂载到 Vue 根实例上。

import { createApp } from 'vue'
import App from './App.vue'
import { createPinia } from 'pinia'

const app = createApp(App)
/** 创建 pinia 实例 */
const pinia = createPinia()

/** 挂载到 Vue 根实例上 */
app.use(pinia)
app.mount('#app')

创建 Store

首先在 src 下创建 store/index.ts 文件,然后通过 defineStore 对仓库进行配置。

import { defineStore } from 'pinia'

/** main 相当于为容器起一个名字 */
export const useStore = defineStore('main', {
  state: () => {
    return {
      msg: 'Hello World'
    }
  },
  getters: {},
  actions: {} 
})

现在每个页面和组件都可以通过 Pinia 方法读取到全局状态 msg

state: 用来存储全局状态数据。
getters:相当于组件中的计算属性,有缓存功能。
actions:用来修改全局状态数据。


在组件中读取 Store 数据

在 CostPaymentList 组件中,通过 store/index.ts 文件暴露出来的 useStore 得到 store 实例,就可以在组件里使用 Store 里面的全局状态了。

<template>
 <h1>CostPaymentList Page</h1>

 <!-- 在 template 中,有两种方式可以获取 Store 中的全局状态 -->
 <p>{{ store.msg }}</p>
 <p>{{ store.$state.msg }}</p>
</template>

<script lang="ts">
import { defineComponent } from 'vue'
import { useStore } from '../../store/index'

export default defineComponent({
 name: 'CostPaymentList',
 setup() {
   const store = useStore()

   /** 在 setup 中,有两种方式可以获取 Store 中的全局状态 */
   console.log(store.msg)
   console.log(store.$state.msg)

   return {
     store
   }
 },
})
</script>

打印一下 store

image


在组件中修改 Store 数据

  1. 首先在 Store 仓库中添加一个 count 全局状态。
import { defineStore } from 'pinia'

export const useStore = defineStore('main', {
 state: () => {
   return {
     count: 0
   }
 }
})
  1. 创建一个新的组件,并为页面中的按钮添加 click 事件,在事件里面修改全局状态 count
<template>
  <h1>HelloWorld Page</h1>

  <p>{{ store.count }}</p>
  <button @click="handleAddCount">增加</button>
</template>

<script lang="ts">
import { defineComponent } from 'vue'
import { useStore } from '../store/index'

export default defineComponent({
  name: 'HelloWorld',
  setup() {
    const store = useStore()

    const handleAddCount = () => {
      store.count++
    }

    return { store, handleAddCount }
  }
})
</script>
  1. 把新创建的组件引入到 App 组件中。
<template>
  <CostPaymentList />
  <hr />
  <HelloWorld />
</template>

<script lang="ts">
import { defineComponent } from 'vue'
import CostPaymentList from 'views/finance/CostPaymentList.vue'
import HelloWorld from './components/HelloWorld.vue'

export default defineComponent({
  name: 'App',
  components: {
    CostPaymentList,
    HelloWorld
  }
})
</script>

当点击按钮时,两个组件的数据都会同时变化。

image


对 Store 进行解构

在 CostPaymentList 组件对 Store 进行解构,并在 template 中直接使用解构后的数据。

<template>
  <h1>CostPaymentList Page</h1>

  <p>{{ count }}</p>
</template>

<script lang="ts">
import { defineComponent } from 'vue'
import { useStore } from '../../store/index'

export default defineComponent({
  name: 'CostPaymentList',
  setup() {
    const { count } = useStore()

    return {
      count
    }
  },
})
</script>

然后点击 Hello World 组件中的按钮,发现 CostPaymentList 组件中的数据并未同步更新。

image

打印一下解构后的 count

console.log({count}, typeof count)

image

可以发现 countnumber 类型,并非是响应式结构,所以才会丢失响应。

如果需要解构后还保持响应式,需要使用到 pinia 暴露出来的 storeToRefs

<template>
  <h1>CostPaymentList Page</h1>

  <p>{{ count }}</p>
</template>

<script lang="ts">
import { defineComponent } from 'vue'
import { storeToRefs } from 'pinia'
import { useStore } from '../../store/index'

export default defineComponent({
  name: 'CostPaymentList',
  setup() {
    const store = useStore()
    const { count } = storeToRefs(store)

    return {
      count
    }
  },
})
</script>

通过 storeToRefs 可以使解构出来的数据拥有响应能力。

image

打印一下 count

image

可以看到,此时的 count 是作了 ref 响应式处理。


修改 Store 状态数据的多种方式

修改 Store 中状态方式除了上面所说的方式,还有三种方式。

  1. 通过 $patch 同时修改多个状态。
<template>
  <h1>HelloWorld Page</h1>

  <p>{{ count }}</p>
  <p>{{ name }}</p>
  <button @click="handleUpdateStore">更新</button>
</template>

<script lang="ts">
import { defineComponent } from 'vue'
import { storeToRefs } from 'pinia'
import { useStore } from '../store/index'

export default defineComponent({
  name: 'HelloWorld',
  setup() {
    const store = useStore()

    const handleUpdateStore = () => {
      store.$patch({
        count: 100,
        name: 'new_sjw'
      })
    }

    return { ...storeToRefs(store), handleUpdateStore }
  }
})
</script>

image

如果想同时修改多个状态,一定要使用 $patch 吗?

并不是,通过多条语句也可以达到这个目的:

const handleUpdateStore = () => {
      store.count = 100
      store.name = 'new _sjw'
    }

之所以使用 $patch,是因为 Pinia 的官方网站,已经明确表示 $patch 的方式是经过优化的,会加快修改速度,对程序的性能有很大的好处。所以如果是多个状态同时更新据,推荐使用 $patch 方式更新。


  1. 通过 $patch 加函数的形式修改状态

第一种方式中,$patch 接收一个对象,适合基本数据类型的修改,不适合复杂数据的修改(如数组、对象)。

针对这个问题,$patch 可以通过接收一个函数来处理复杂数据:

import { defineStore } from 'pinia'

export const useStore = defineStore('main', {
  state: () => {
    return {
      count: 0,
      name: 'sjw',
      list: [
        { des: '1 号篮球员' },
        { des: '2 号篮球员' },
        { des: '3 号篮球员' }
      ]
    }
  }
})
<template>
  <h1>HelloWorld Page</h1>

  <p>{{ count }}</p>
  <p>{{ name }}</p>
  <ul>
    <li v-for="(item, index) in list" :key="index">{{ item.des }}</li>
  </ul>
  <button @click="handleUpdateStore">更新</button>
</template>

<script lang="ts">
import { defineComponent } from 'vue'
import { storeToRefs } from 'pinia'
import { useStore } from '../store/index'

export default defineComponent({
  name: 'HelloWorld',
  setup() {
    const store = useStore()

    const handleUpdateStore = () => {
      store.$patch((state) => {
        /** 在这里不要对 state 进行解构,否则会丢失响应式 */
        state.count++
        state.name = 'new_sjw'
        state.list[0].des = '200 号足球员'
      })
    }

    return { ...storeToRefs(store), handleUpdateStore }
  }
})
</script>

  1. 通过调用 actions 来修改状态

对于复杂的修改操作,可以在 actions 中封装一个方法,然后在组件调用该方法即可。

import { defineStore } from 'pinia'

export const useStore = defineStore('main', {
  state: () => {
    return {
      count: 0,
      name: 'sjw',
      list: [
        { des: '1 号篮球员' },
        { des: '2 号篮球员' },
        { des: '3 号篮球员' }
      ]
    }
  },
  actions: {
    changeState() {
      this.count++
      this.name = 'new_sjw'
      this.list[0].des = '200 号足球员'
    }
  }
})
<script lang="ts">
import { defineComponent } from 'vue'
import { storeToRefs } from 'pinia'
import { useStore } from '../store/index'

export default defineComponent({
  name: 'HelloWorld',
  setup() {
    const store = useStore()

    const handleUpdateStore = () => {
      store.changeState()
    }

    return { ...storeToRefs(store), handleUpdateStore }
  }
})
</script>

在使用 actions 的时候,不能时候箭头函数,否则 thisundefined


Getters 的使用

Pinia 中的 Getter 和组件中的计算属性(Computer)作用几乎一样,可以对 State 里面的状态进一步处理。

  1. 首先在 Store 中声明一个 Getter 属性,它对 msg 进行了处理。
import { defineStore } from 'pinia'

export const useStore = defineStore('main', {
  state: () => {
    return {
      msg: 'Hello'
    }
  },
  getters: {
    logMsg(state) {
      return state.msg + ' Wold!'
    }
  }
})
  1. 在组件中的使用方式和 state 里面的状态一样,直接调用即可。
<template>
  <h1>CostPaymentList Page</h1>

  <p>{{ logMsg }}</p>
</template>

<script lang="ts">
import { defineComponent } from 'vue'
import { storeToRefs } from 'pinia'
import { useStore } from '../../store/index'

export default defineComponent({
  name: 'CostPaymentList',
  setup() {
    const store = useStore()
    const { logMsg } = storeToRefs(store)

    return {
      logMsg
    }
  },
})
</script>

验证 Getter 的缓存特性:

1.首先在 logMsg 中添加 console.log() 语句,保证 logMsg 每次被调用时都能知道。

import { defineStore } from 'pinia'

export const useStore = defineStore('main', {
  state: () => {
    return {
      msg: 'Hello'
    }
  },
  getters: {
    logMsg(state) {
      console.log('调用了 logMsg')
      return state.msg + ' Wold!'
    }
  }
})
  1. 然后在组件中编写一个方法,使其能够多次调用 logMsg
<template>
  <h1>CostPaymentList Page</h1>

  <button @click="handleLogMsg">调用</button>
</template>

<script lang="ts">
import { defineComponent } from 'vue'
import { storeToRefs } from 'pinia'
import { useStore } from '../../store/index'

export default defineComponent({
  name: 'CostPaymentList',
  setup() {
    const store = useStore()
    const { logMsg } = storeToRefs(store)

    const handleLogMsg = () => {
      console.log(logMsg.value)
    }

    return {
      logMsg,
      handleLogMsg
    }
  },
})
</script>

image

可以发现,多次调用 logMsg 时,只打印了一次“调用了 logMsg”,说明 logMsg 真正被调用次数的只有一次,后续的调用都是从缓存中返回数据。

  1. 在组件中编写一个可以更改 msg 状态的方法。
<template>
  <h1>CostPaymentList Page</h1>

  <button @click="handleLogMsg">调用</button>
  <button @click="handleChangeMsg">更改</button>
</template>

<script lang="ts">
import { defineComponent } from 'vue'
import { storeToRefs } from 'pinia'
import { useStore } from '../../store/index'

export default defineComponent({
  name: 'CostPaymentList',
  setup() {
    const store = useStore()
    const { msg, logMsg } = storeToRefs(store)

    const handleLogMsg = () => {
      console.log(logMsg.value)
    }

    const handleChangeMsg = () => {
      msg.value += 'sjw-'
      console.log(logMsg.value)
    }

    return {
      logMsg,
      handleLogMsg,
      handleChangeMsg
    }
  },
})
</script>

image

可以发现,当 msg 发生变化时,logMsg 都会随着执行一次,清除之前的缓存。

也可以在 Getters 中使用 this。


Store 的互相调用

当项目存在多个 Store 时,这些 Store 之间可以互相调用。

  1. 首先,新增一个新的 userStore,并声明一个 storeName 状态。
/* user.ts */
import { defineStore } from 'pinia'

export const userStore = defineStore('user', {
  state: () => {
    return {
      storeName: 'userStore'
    }
  }
})
  1. 在其它 Store 中引入 userStore 并调用 storeName 属性。
/* index.ts */
import { defineStore } from 'pinia'
import { userStore } from './user'

export const useStore = defineStore('main', {
  state: () => ({
    msg: 'Hello'
  }),
  actions: {
    getUserStore() {
      console.log(userStore().storeName)
    }
  }
})

学习平台:
Pinia入门视频教程 全新一代状态管理工具Pinia -Vue3全家桶系列

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值