Pinia 简介

Pinia 是 Vue 专属的最新状态管理库 ,是 Vuex 状态管理工具的替代品

Vue Pinia官方库

  1. 提供更加简单的API (去掉了 mutation )
  2. 提供符合,组合式风格的API (和 Vue3 新语法统一)
  3. 去掉了 modules 的概念,每一个 store 都是一个独立的模块
  4. 配合 TypeScript 更加友好,提供可靠的类型推断

Pinia 使用

手动添加

  1. 用你喜欢的包管理器安装 pinia
1
2
npm install pinia
yarn add pinia
  1. 创建一个 pinia 实例 (根 store) 并将其传递给应用:
1
2
3
4
5
6
7
8
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'

const pinia = createPinia()
const app = createApp(App)

app.use(pinia).mount('#app') // 支持链式

定义

  1. 创建 src/store 目录,用于存放 store 数据
  2. 在数据文件中定义 store
  • 与 Vue 组合式 API 的 setup 函数相似,我们可以传入一个函数,该函数定义了一些响应式属性和方法,并且返回一个带有我们想暴露出去的属性和方
  • ref() 就是 state 属性
  • computed() 就是 getters
  • function() 就是 actions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { defineStore } from "pinia";
import { computed, ref } from "vue";

export const useCounterStore = defineStore('counter', () => {
const count = ref(100)
const doubleCount = computed(() => count.value * 2)
const addCount = () => {count.value++}
const subCount = () => {count.value--}
return {
count,
doubleCount,
addCount,
subCount
}
})
  • 也有 Option Store 定义方式,这种非常麻烦难看懂,我不喜欢
  • 你可以认为 state 是 store 的数据 (data),getters 是 store 的计算属性 (computed),而 actions 则是方法 (methods)。
1
2
3
4
5
6
7
8
9
10
11
export const useCounterStore = defineStore('counter', { 
state: () => ({ count: 0 }),
getters: {
double: (state) => state.count * 2,
},
actions: {
increment() {
this.count++
},
},
})
  1. 在组件中调用 store
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<!-- 顶层组件App.vue -->
<script setup>
import middleCom from '@/components/middleCom.vue'
import bottomCom from '@/components/bottomCom.vue'
import { useCounterStore } from '@/store/counter'
const counter = useCounterStore()
</script>

<template>
<h1>顶层数据: {{ counter.count }}</h1>
<h2>双倍结果: {{ counter.doubleCount }}</h2>
<middleCom></middleCom>
<bottomCom></bottomCom>
</template>

<!-- 子组件middleCom.vue -->
<script setup>
import { useCounterStore } from '@/store/counter';
const counter = useCounterStore()
</script>

<template>
<div>
中层数据 - {{ counter.count }}
<button @click="counter.addCount">+1</button>
</div>
</template>

<!-- 子组件bottomCom.vue -->
<script setup>
import { useCounterStore } from '@/store/counter';
const counter = useCounterStore()
</script>

<template>
<div>
底层数据 - {{ counter.count }}
<button @click="counter.subCount">-1</button>
</div>
</template>

image.png

action 异步实现

在 pinia 中,不再像 Vuex 那样需要考虑 mutations/actions 实现同步/异步,现在的 actions 都可以实现,同步实现在上面的代码中已经写了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { defineStore } from "pinia";
import { ref } from "vue";
import axios from "axios";

export const useChannelStore = defineStore('channel', () => {
const channelList = ref([])
const getList = async () => {
const obj = await axios.get('http://geek.itheima.net/v1_0/channels')
channelList.value = obj.data.data.channels
console.log(channelList.value);
}
return {
channelList,
getList
}
})

storeToRefs 工具函数

在调用数据时,有时候需要解构库数据,如果直接解构会导致数据失去响应式特性

使用 storeToRefs 工具函数,可以辅助保持数据 (state+getter)的响应式解构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<script>
import middleCom from '@/components/middleCom.vue'
import bottomCom from '@/components/bottomCom.vue'
import { storeToRefs } from 'pinia'
import { useCounterStore } from '@/store/counter'
import { useChannelStore } from '@/store/channel'
const counter = useCounterStore()
const channel = useChannelStore()
// 直接解构会导致数据失去响应式特性
// const { count, doubleCount } = counter
const { count, doubleCount } = storeToRefs(counter)
const { getList } = channel // 不过函数方法不需要响应式所有可以直接解构
const { channelList} = storeToRefs(channel)
</script>

<template>
<h1>顶层数据: {{ count }}</h1>
<h2>双倍结果: {{ doubleCount }}</h2>
<middleCom></middleCom>
<bottomCom></bottomCom>
<div>
<button @click="getList">获取列表</button>
<ul>
<li v-for="item in channelList" :key="item.id">{{ item.name }}</li>
</ul>
</div>
</template>

Pinia 数据持久化插件

官方文档:https://prazdevs.github.io/pinia-plugin-persistedstate/zh/

  1. 选择喜欢的包管理器安装依赖:
1
2
3
npm i pinia-plugin-persistedstate
yarn add pinia-plugin-persistedstate
pnpm i pinia-plugin-persistedstate
  1. 在 main.js 中,将插件添加到 pinia 实例上
1
2
3
4
5
6
7
8
9
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
import App from './App.vue'

const pinia = createPinia()
const app = createApp(App)
pinia.use(piniaPluginPersistedstate) // 因为是 pinia 的插件,所有是给 pinia
app.use(pinia).mount('#app')
  1. 创建 Store 时,将 persist 选项设置为 true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { defineStore } from "pinia";
import { computed, ref } from "vue";

export const useCounterStore = defineStore('counter', () => {
const count = ref(100)
const doubleCount = computed(() => count.value * 2)
const addCount = () => {count.value++}
const subCount = () => {count.value--}
return {
count,
doubleCount,
addCount,
subCount
}
},{
persist: true
})