《Pinia的使用》

Pinia 全局状态管理工具

Pinia.js 有如下特点:

  • 完整的 ts 的支持;
  • 足够轻量,压缩后的体积只有 1kb 左右;
  • 去除 mutations,只有 state,getters,actions;
  • actions 支持同步和异步;
  • 代码扁平化没有模块嵌套,只有 store 的概念,store 之间可以自由使用,每一个 store 都是独立的
  • 无需手动添加 store,store 一旦创建便会自动添加;

官方文档 Pinia

起步

1
2
3
4
yarn add pinia

npm install pinia

引入注册 Vue3

1
2
3
4
5
6
7
import { createApp } from "vue";
import App from "./App.vue";
import { createPinia } from "pinia";
const store = createPinia();
let app = createApp(App);
app.use(store);
app.mount("#app");

vue2 使用

1
2
3
4
5
6
7
8
9
10
11
12
13
import { createPinia, PiniaVuePlugin } from "pinia";

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

new Vue({
el: "#app",
// other options...
// ...
// note the same `pinia` instance can be used across multiple Vue apps on
// the same page
pinia,
});

初始化仓库 Store

  1. 新建一个文件夹 Store

  2. 新建文件[name].ts

  3. 定义仓库 Store

1
import { defineStore } from "pinia";
  1. 我们需要知道存储是使用定义的 defineStore(),并且它需要一个唯一的名称,作为第一个参数传递

我这儿名称抽离出去了

新建文件 store-namespace/index.ts

1
2
3
export const enum Names {
Test = 'TEST'
}

store 引入

1
2
3
import { defineStore } from "pinia";
import { Names } from "./store-namespace";
export const useTestStore = defineStore(Names.Test, {});

这个名称,也称为 id,是必要的,Pania 使用它来将商店连接到 devtools。将返回的函数命名为 use…是可组合项之间的约定,以使其使用习惯。

  1. 定义值

State 箭头函数 返回一个对象 在对象里面定义值

1
2
3
4
5
6
7
8
9
10
import { defineStore } from "pinia";
import { Names } from "./store-namespce";

export const useTestStore = defineStore(Names.Test, {
state: () => {
return {
current: 1,
};
},
});
1
2
3
4
5
6
7
8
9
10
11
12
13
import { defineStore } from "pinia";
import { Names } from "./store-namespce";
export const useTestStore = defineStore(Names.Test, {
state: () => {
return {
current: 1,
};
},
//类似于computed 可以帮我们去修饰我们的值
getters: {},
//可以操作异步 和 同步提交state
actions: {},
});

state

  1. State 是允许直接修改值的 例如 current++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template>
<div>
<button @click="Add">+</button>
<div>
{{Test.current}}
</div>
</div>
</template>
<script setup lang='ts'>
import {useTestStore} from './store'
const Test = useTestStore()
const Add = () => {
Test.current++
}
</script>
<style>
</style>
  1. 批量修改 State 的值
    在他的实例上有$patch 方法可以批量修改多个值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<template>
<div>
<button @click="Add">+</button>
<div>
{{Test.current}}
</div>
<div>
{{Test.age}}
</div>
</div>
</template>
<script setup lang='ts'>
import {useTestStore} from './store'
const Test = useTestStore()
const Add = () => {
Test.$patch({
current:200,
age:300
})
}
</script>
<style>
</style>
  1. 批量修改函数形式

推荐使用函数形式 可以自定义修改逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<template>
<div>
<button @click="Add">+</button>
<div>
{{Test.current}}
</div>
<div>
{{Test.age}}
</div>
</div>
</template>
<script setup lang='ts'>
import {useTestStore} from './store'
const Test = useTestStore()
const Add = () => {
Test.$patch((state)=>{
state.current++;
state.age = 40
})
}
</script>
<style>
</style>
  1. 通过原始对象修改整个实例

$state您可以通过将 store 的属性设置为新对象来替换store的整个状态

缺点就是必须修改整个对象的所有属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<template>
<div>
<button @click="Add">+</button>
<div>
{{Test.current}}
</div>
<div>
{{Test.age}}
</div>
</div>
</template>
<script setup lang='ts'>
import {useTestStore} from './store'
const Test = useTestStore()
const Add = () => {
Test.$state = {
current:10,
age:30
}
}
</script>
<style>
</style>
  1. 通过 actions 修改

定义 Actions

actions中直接使用 this 就可以指到 state 里面的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { defineStore } from "pinia";
import { Names } from "./store-naspace";
export const useTestStore = defineStore(Names.TEST, {
state: () => {
return {
current: 1,
age: 30,
};
},

actions: {
setCurrent() {
this.current++;
},
},
});

使用方法直接在实例调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<template>
<div>
<button @click="Add">+</button>
<div>
{{Test.current}}
</div>
<div>
{{Test.age}}
</div>
</div>
</template>
<script setup lang='ts'>
import {useTestStore} from './store'
const Test = useTestStore()
const Add = () => {
Test.setCurrent()
}
</script>
<style>
</style>

解构 store

在 Pinia 是不允许直接解构是会失去响应性的

1
2
3
const Test = useTestStore();
const { current, name } = Test;
console.log(current, name);

差异对比
修改 Test current 解构完之后的数据不会变
而源数据是会变的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
<div>origin value {{Test.current}}</div>
<div>
pinia:{{ current }}--{{ name }}
change :
<button @click="change">change</button>
</div>
</template>
<script setup lang='ts'>
import { useTestStore } from './store'
const Test = useTestStore()
const change = () => {
Test.current++
}
const { current, name } = Test
console.log(current, name);
</script>
<style>
</style>

解决方案可以使用 storeToRefs

1
2
3
import { storeToRefs } from "pinia";
const Test = useTestStore();
const { current, name } = storeToRefs(Test);

Actions

Actions(支持同步异步)

  1. 同步 直接调用即可
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { defineStore } from "pinia";
import { Names } from "./store-naspace";
export const useTestStore = defineStore(Names.TEST, {
state: () => ({
counter: 0,
}),
actions: {
increment() {
this.counter++;
},
randomizeCounter() {
this.counter = Math.round(100 * Math.random());
},
},
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template>
<div>
<button @click="Add">+</button>
<div>
{{Test.counter}}
</div>
</div>
</template>
<script setup lang='ts'>
import {useTestStore} from './store'
const Test = useTestStore()
const Add = () => {
Test.randomizeCounter()
}
</script>
<style>
</style>
  1. 异步 可以结合 async await 修饰
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
import { defineStore } from 'pinia'
import { Names } from './store-naspace'
type Result = {
name: string
isChu: boolean
}
const Login = (): Promise<Result> => {
return new Promise((resolve) => {
setTimeout(() => {
resolve({
name: '小满',
isChu: true
})
}, 3000)
})
}
export const useTestStore = defineStore(Names.TEST, {
state: () => ({
user: <Result>{},
name: "123"
}),
actions: {
async getLoginInfo() {
const result = await Login()
this.user = result;
}
},
})

template

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template>
<div>
<button @click="Add">test</button>
<div>
{{Test.user}}
</div>
</div>
</template>
<script setup lang='ts'>
import {useTestStore} from './store'
const Test = useTestStore()
const Add = () => {
Test.getLoginInfo()
}
</script>
<style>
</style>
  1. 多个 action 互相调用 getLoginInfo setName
1
2
3
4
5
6
7
8
9
10
11
12
13
14
state: () => ({
user: <Result>{},
name: "default"
}),
actions: {
async getLoginInfo() {
const result = await Login()
this.user = result;
this.setName(result.name)
},
setName (name:string) {
this.name = name;
}
},

getters

  1. 使用箭头函数不能使用 this this 指向已经改变指向 undefined 修改值请用 state

主要作用类似于 computed 数据修饰并且有缓存

1
2
3
getters:{
newPrice:(state)=> `$${state.user.price}`
},
  1. 普通函数形式可以使用 this
1
2
3
4
5
getters:{
newCurrent ():number {
return ++this.current
}
},
  1. getters 互相调用
1
2
3
4
5
6
7
8
getters:{
newCurrent ():number | string {
return ++this.current + this.newName
},
newName ():string {
return `$-${this.name}`
}
},

相关 api

  1. $reset
    重置 store 到他的初始状态
1
2
3
4
5
state: () => ({
user: <Result>{},
name: "default",
current:1
}),

Vue 例如我把值改变到了 10

1
2
3
const change = () => {
Test.current++;
};

调用$reset();

将会把 state 所有值 重置回 原始状态;

  1. 订阅 state 的改变
    类似于 Vuex 的 abscribe 只要有 state 的变化就会走这个函数
1
2
3
Test.$subscribe((args, state) => {
console.log(args, state);
});

第二个参数

如果你的组件卸载之后还想继续调用请设置第二个参数

1
2
3
4
5
6
7
8
Test.$subscribe(
(args, state) => {
console.log(args, state);
},
{
detached: true,
}
);
  1. 订阅 Actions 的调用

只要有 actions 被调用就会走这个函数

1
2
3
Test.$onAction((args) => {
console.log(args);
});