Vue3之状态管理器(Pinia)详解及使用方式
作者:明天也要努力
注意:本文项目使用脚手架为 Vite;
1. 前言
Pinia 对比 Vuex
- Pinia 同时支持 Vue2 以及 Vue3 ,这让同时使用两个版本的小伙伴更容易上手;
- Pinia 中只存在 State,getter,action,剔除掉了 Vuex 中的 Mutation 及 Module;
- Pinia 中的 action 可同时支持同步任务、异步任务;
- 更友好的支持了 TypeScript ,无需创建自定义复杂包装器来支持 TypeScript,所有内容都是类型化的,并且 API 的设计方式尽可能利用 TS 类型推断;
- Pinia 在修改状态的时候不需要通过其他 api,如:vuex 需通过 commit,dispatch 来修改,所以在语法上比 vuex 更容易理解和使用灵活;
- 由于去除掉了 Module ,无需再创建各个模块嵌套了。Vuex 中,如果数据过多,通常会通过划分模块来进行管理,而 Pinia 中,每个 Store 都是独立的,互不影响;
- 支持服务端渲染;
2. 安装及引入
yarn add pinia // 或者使用 npm npm install pinia
安装完 Pinia 包之后,需要在 main.js 文件中导入 createPinia 函数并将 Pinia 插件与 Vue 应用程序绑定:
import { createApp } from 'vue'; import App from './App.vue'; // 引入 createPinia 函数 import { createPinia } from 'pinia'; const app = createApp(App) // 使用 createPinia() 来创建 Pinia(根存储),并应用到整个应用中 app.use(createPinia()); app.mount('#app');
使用 createPinia() 函数创建并初始化 Pinia 插件实例,将其与 Vue 应用程序绑定使用 app.use(pinia)。
至此,我们就可以使用Pinia 来管理 Vue 应用程序的状态了。
最后,在 src 文件下创建一个 store 文件夹,并添加 store.js 文件。
3. Pinia的使用Store
Store
是使用 defineStore() 定义的,并且它需要一个唯一名称,作为第一个参数传递。
这个名字 ,也被用作 id 是必须传入的, Pinia 将用它来连接 store 和 devtools。
将返回的函数命名为 use… 是跨可组合项的约定,以使其符合使用习惯。
State
State 是 store 中存储数据的地方。
通过定义 State,可以在 store 的任何位置访问和修改数据。
// store/store.js import { defineStore } from 'pinia'; export const useMainStore = defineStore('main',{ state: () => { return { count:0 } } })
// views/home.vue <template> <div class="count">state:{{mainStore.count}}</div> <div class="btnWrap"> <button @click="resetStore">重 置</button> </div> </template> <script setup> import {useMainStore} from '@/store/store.js'; const mainStore = useMainStore(); console.log(mainStore.count) // 0 const resetStore = () => { mainStore.$reset() } </script>
效果:
Getters
Getter 用来获取从 state 派生的数据,类似于 Vue 组件中的 computed 计算属性。
可通过 defineStore() 中的 getters 属性来定义它们。
推荐使用箭头函数,并且它将接收 state 作为第一个参数:
export const useStore = defineStore('main', { state: () => ({ count: 0, }), getters: { doubleCount: (state) => state.count * 2, }, })
Actions
Action 相当于组件中的方法。
它可以通过 defineStore() 中的 actions 属性来定义;
Action 是一种将异步操作封装在 store 中的方式,它是一个可被调用的函数,也可接收参数并修改 store 中的状态。
import { defineStore } from 'pinia' export const myStore = defineStore('myStore',{ state: () => ({ message: 'Hello', }), actions: { async fetchMessage() { const res = await fetch('http://127.0.0.1:5173/message') this.message = res.message }, }, })
4. 示例完整代码
4.1 选项式写法
// store/store.js import { defineStore } from 'pinia'; import axios from 'axios'; export const useMainStore = defineStore('main',{ state: () => { return { count:0, count2:0, list:[], } }, getters:{ doubleCount(){ return this.count*2; } }, actions:{ add(){ this.count++; }, update(val){ this.count = val.value; }, add2(){ this.count2++; }, // 异步 async getList(){ const res = await axios.get('https://api.oioweb.cn/api/common/history'); if(res.data.code == 200){ this.list = res.data.result || []; } }, } })
在组件中使用
<template> <div class="count">state:{{mainStore.count}}</div> <div class="count">getters:{{mainStore.doubleCount}}</div> <div class="btnWrap"> <button @click="resetStore">重 置</button> <button @click="addCount">增 加</button> <button @click="updateCount">更新至100</button> </div> <hr/> <div class="count">state:{{count2}}</div> <div class="btnWrap"> <button @click="add2">增 加</button> </div> <hr/> <h3>历史上的今天</h3> <ul class="list"> <li v-for="(item,index) in mainStore.list" :key="index"> {{item.year}}年 - {{item.title}} </li> </ul> </template>
<script setup> import {useMainStore} from '@/store/store.js'; import {onMounted,ref} from 'vue'; import {storeToRefs} from 'pinia'; const mainStore = useMainStore(); const {count2} = storeToRefs(mainStore); const {add2} = mainStore; console.log(mainStore) const number = ref(100); const resetStore = () => { mainStore.$reset(); } const addCount = () => { mainStore.add(); }; const updateCount = () => { mainStore.update(number); } onMounted(() => { mainStore.getList(); }) </script>
效果:
分别触发 add2 两次,addCount、getList 一次后的效果
4.2 组合式写法
在组合式 API 中:
- ref() 相当于 state 属性;
- computed() 相当于 getters;
- function() 相当于 actions;
// store/count.js import { defineStore } from 'pinia'; import {computed, ref} from 'vue'; import axios from 'axios'; // 第一个参数是应用中 Store 的唯一 ID export const useCountStore = defineStore('count',() => { // state const count = ref(0); const count2 = ref(0); const list = ref([]); // getter const doubleCount = computed(() => { return count.value*2 }) // 同步action const add = () => { count.value++; } const update = (val) =>{ count.value = val.value; } const add2 = () => { count2.value++; } // 异步action const getList = async () => { const res = await axios.get('https://api.oioweb.cn/api/common/history'); if(res.data.code == 200){ list.value = res.data.result || []; } } return{ count, count2, doubleCount, list, add, update, add2, getList, } })
在组件中使用
<template> <div class="count">state:{{countStore.count}}</div> <div class="count">getters:{{countStore.doubleCount}}</div> <div class="btnWrap"> <button @click="resetStore">重 置</button> <button @click="addCount">增 加</button> <button @click="updateCount">更新至100</button> </div> <hr/> <div class="count">state:{{count2}}</div> <div class="btnWrap"> <button @click="add2">增 加</button> </div> <hr/> <h3>历史上的今天</h3> <ul class="list"> <li v-for="(item,index) in countStore.list" :key="index"> {{item.year}}年 - {{item.title}} </li> </ul> </template>
<script setup> import {useCountStore} from '@/store/count.js'; import {onMounted,ref} from 'vue'; import {storeToRefs} from 'pinia'; const countStore = useCountStore(); const {count2} = storeToRefs(countStore); const {add2} = countStore; console.log(countStore) const number = ref(100); const resetStore = () => { countStore.$reset(); } const addCount = () => { countStore.add(); }; const updateCount = () => { countStore.update(number); } onMounted(() => { countStore.getList(); }) </script>
效果:
分别触发 add2 两次,addCount、getList 一次后的效果
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。