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", pinia, });
|
初始化仓库 Store
新建一个文件夹 Store
新建文件[name].ts
定义仓库 Store
1
| import { defineStore } from "pinia";
|
- 我们需要知道存储是使用定义的 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…是可组合项之间的约定,以使其使用习惯。
- 定义值
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, }; }, getters: {}, actions: {}, });
|
state
- 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>
|
- 批量修改 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 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>
|
- 通过原始对象修改整个实例
$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>
|
- 通过 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 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>
|
- 异步 可以结合 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>
|
- 多个 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
- 使用箭头函数不能使用 this this 指向已经改变指向 undefined 修改值请用 state
主要作用类似于 computed 数据修饰并且有缓存
1 2 3
| getters:{ newPrice:(state)=> `$${state.user.price}` },
|
- 普通函数形式可以使用 this
1 2 3 4 5
| getters:{ newCurrent ():number { return ++this.current } },
|
- getters 互相调用
1 2 3 4 5 6 7 8
| getters:{ newCurrent ():number | string { return ++this.current + this.newName }, newName ():string { return `$-${this.name}` } },
|
相关 api
- $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 所有值 重置回 原始状态;
- 订阅 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, } );
|
- 订阅 Actions 的调用
只要有 actions 被调用就会走这个函数
1 2 3
| Test.$onAction((args) => { console.log(args); });
|