安装

1
2
3
yarn add pinia
# 或者使用 npm
npm install pinia
1
2
# 如果你的应用使用的 Vue 版本低于 2.7,你还需要安装组合式 API 包:@vue/composition-api。
npm install @vue/composition-api
1
2
3
4
5
6
7
8
9
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'

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

app.use(pinia)
app.mount('#app')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 如果是vue2 还需要安装插件
import { createPinia, PiniaVuePlugin } from 'pinia'

Vue.use(PiniaVuePlugin)
const pinia = createPinia()

new Vue({
el: '#app',
// 其他配置...
// ...
// 请注意,同一个`pinia'实例
// 可以在同一个页面的多个 Vue 应用中使用。
pinia,
})

开始使用

创建全局状态

首先按照国际惯例, 会在项目的 src 目录下创建 store/index.js 文件

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
// store/index.js
import { defineStore } from 'pinia';
// 你可以对 `defineStore()` 的返回值进行任意命名,
// 但最好使用 store 的名字,同时以 `use` 开头且以 `Store` 结尾。
// (比如 `useUserStore`,`useCartStore`,`useProductStore`)
// 第一个参数是你的应用中 Store 的唯一 ID。
export const useCounterStore = defineStore("counter", {
/**
* 用来存储全局状态 类似于组件的 data
* 1. 必须是函数: 避免服务端渲染交叉请求导致数据状态污染
* 2. 必须是箭头函数: 更好的 ts 类型推导
*/
state: () => ({
count: 0,
text: "hahaha"
}),
/**
* 类似于组件的 computed, 用于封装计算属性, 有缓存功能
*/
getters: {
double: (state) => state.count * 2,
},
/**
* 类似于组件的 methods, 封装业务逻辑, 修改 state
*/
actions: {
changeState(a: number, b: string) {
this.count+=a;
this.test = b;
},
},
})

模板中如何使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
<p>{{ counterStore.count }}</p>
<hr />
<p>{{ count }}</p>
<button @click="add">修改count</button>
</template>

<script setup>
import { storeToRefs } from "pinia"
import { useCounterStore } from "@/store"
const counterStore = useCounterStore();
// 这种结构赋值的方法是一次性的, 因为它不是响应式的, 监听不到值的改变
// const { count } = counterStore;
// 怎样使它具有响应式, 使用 pinia 中的 storeToRefs 方法
const { count } = storeToRefs(counterStore);
const add = ()=>{
counterStore.count++
}
</script>

修改 state 中的值

1
2
3
import { storeToRefs } from 'pinia';
import { useCounterStore } from "@/store";
const counterStore = useCounterStore();

方式 1: 直接修改

1
2
3
4
const handleChangeState = ()=>{
counterStore.count++
counterStore.text = "呦呦呦"
}

方式 2: 如果需要修改多个值, 建议使用 $patch 批量更新

$patch 与普通的多次修改有什么区别?

普通的多次修改是批量提交, $patch 是内部处理后一次提交

1
2
3
4
5
6
const handleChangeState = ()=>{
counterStore.$patch({
count: counterStore.count+1,
text: "哎呦喂"
})
}

方式 3: 更好的批量更新方式 $patch , 一个函数

1
2
3
4
5
6
const handleChangeState = ()=>{
counterStore.$patch(state=>{
state.count++;
state.text = "笑脸给多了?"
})
}

方式 4: 逻辑较多时可以封装到 actions 做处理

1
2
3
4
// 组件中
const handleChangeState = ()=>{
counterStore.changeState(1, "你算个der?")
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// store/index.js
import { defineStore } from 'pinia';
export const useCounterStore = defineStore("counter", {
state: () => ({
count: 0,
text: "hahaha"
}),
// 相当于methods, 直接调用, 可以接受参数
// 注意不要使用箭头函数, 因为函数内是使用 this 访问 state 中的数据
actions: {
changeState(a: number, b: string) {
this.count+=a;
this.test = b;
},
},
})

getters 的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// store/index.js
import { defineStore } from 'pinia';
export const useCounterStore = defineStore("counter", {
state: () => ({
count: 0,
test: "加10等于"
}),
/**
* 类似于组件的 computed, 用于封装计算属性, 有缓存功能
*/
getters: {
// 接受一个可选参数: state状态对象
double: (state) => state.count * 2,
count10(state){
return state.count +10
},
// 不接受参数时, 使用 this 访问 state 或其他 getters
// 注意: 在 ts 中, 使用this访问会导致类型推导失败, 需要手动指定返回值的类型
splicText(): string{
return this.count + this.test + this.count10
}
},
})