Skip to content

pinia

Pinia读作皮尼亚, 是 Vue 的存储库,它允许您跨组件/页面共享状态。在vue2的时代我们更多的是使用vuex进行全局管理,但是vue3已经全面拥抱pinia

基本示例

其实在我们使用create-vue脚手架创建好的项目里已经帮我们写好了一个基本示例,位于\src\stores\counter.ts

typescript
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'

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

  return { count, doubleCount, increment }
})

上面的代码为了我们演示了如何创建一个store,你可以理解为定义一个全局可以使用的状态

然后我们就可以在其它的组件/页面里使用它

typescript
import { useCounterStore } from '@/stores/counter'
const counter = useCounterStore()

示例

页面一

vue
<template>
    <div>
        我是页面一,store的值是:{{ counter.count }}
    </div>
    <el-button @click="test">数量自增并跳转到页面2 </el-button>
</template>
<script lang="ts" setup>
import { ElButton } from 'element-plus'
import { useCounterStore } from '@/stores/counter'
import { useRouter } from 'vue-router';
const router = useRouter()
const counter = useCounterStore();
const test = () => {
    counter.increment();
    router.push({ name: 'test2' })
}
</script>

页面二

vue
<template>
    <div>
        我是页面二,store的值是:{{ counter.count }}
        doubleCount:{{ counter.doubleCount }}
    </div>
</template>
<script setup lang="ts">
import { useCounterStore } from '@/stores/counter'
const counter = useCounterStore()

</script>

命名规范

首先Store 是使用 defineStore() 定义的,并且它需要一个唯一名称,作为第一个参数传递:

typescript
import { defineStore } from 'pinia'

// useStore 可以是 useUser、useCart 之类的任何东西
// 第一个参数是应用程序中 store 的唯一 id
export const useStore = defineStore('name', {
  // other options...
})

这个 name,也称为 id 将返回的函数命名为 use... 是跨可组合项的约定,以使其符合你的使用习惯。

一个完整的store是由stategettersactions组成的

State

拿我们上面的基本示例代码举例

typescript
  const count = ref(0)

我们使用ref定义的count变量就可以理解为是piniastate

访问state

默认情况下,我们可以通过 store 实例访问状态来直接读取和写入状态:

typescript
const counter = useCounterStore()

counter.count++

批量修改state

除了直接用 store.counter++ 修改 store,你还可以调用 $patch 方法。 它允许您使用部分“state”对象同时应用多个更改:

typescript
store.$patch({
  counter: store.counter + 1,
  name: 'Abalam',
})

但是,使用这种语法应用某些突变非常困难或代价高昂:任何集合修改(例如,从数组中推送、删除、拼接元素)都需要您创建一个新集合。 正因为如此,$patch 方法也接受一个函数来批量修改集合内部分对象的情况:

typescript
cartStore.$patch((state) => {
  state.items.push({ name: 'shoes', quantity: 1 })
  state.hasChanged = true
})

getters

示例代码里

typescript
  const doubleCount = computed(() => count.value * 2)

这个用计算属性定义的doubleCount就是一个getters,它和计算属性一样,也是只读并且具有缓存的

将参数传递给 getter

Getters 只是幕后的 computed 属性,因此无法向它们传递任何参数。 但是,您可以从 getter 返回一个函数以接受任何参数:

typescript
const test_arr = ref([{
    name: '李雷',
    id: 1
  }, {
    name: '韩梅梅',
    id: 2
  }])

const getUserById = computed(() => (id: number) => test_arr.value.find((f) => f.id === id)?.name)

并在组件中使用:

html
<div>id1的name是:{{ counter.getUserById(1) }}</div>
<div>id2的name是:{{ counter.getUserById(2) }}</div>

请注意,在执行此操作时,getter 不再缓存,它们只是您调用的函数。 但是,您可以在 getter 本身内部缓存一些结果,这并不常见,但应该性能更高:

typescript
  const count = ref(0);

  const test_arr = ref([{
    name: '李雷',
    id: 1
  }, {
    name: '韩梅梅',
    id: 2
  }])

  const getUserById = computed(() => (id: number) => test_arr.value.find((f) => f.id === id)?.name+count.value)

Actions

Actions 相当于组件中的函数。 它们非常适合定义业务逻辑

示例代码里

typescript
  function increment() {
    count.value++
  }

这个自增函数就是一个actions,它的内部可以对某些state做赋值操作,最重要的是actions内部支持异步操作