了解pinia
了解pinia是什么?为什么要使用pinia?它和vuex有什么区别?
首先Pinia是vue官方团队的一名成员开发的状态管理库,pinia名字是西班牙语翻译中文就是菠萝的意思,其设计理念将如果菠萝将状态划分为多个模块,起始于 2019 年 11 月左右的一次实验,其目的是设计一个拥有组合式 API 的 Vue 状态管理库,随着后续的版本迭代pinia逐步实现预案中提到的vuex新版本的大部分功能,也可以看成是vuex的新版本,官方也鼓励将vuex更换为pinia。
为什么要使用pinia?它和vuex有什么区别?
- pinia的兼容性更好。pinia可以支持vue2和vue3两个版本,而
vuex@3.*支持vue2 ,vuex@4.*支持vue3不支持vue2; - pinia打包后体积相比vuex更加轻量。打包后体积很小在实际项目中可以忽略不计,如下图;
- pinia支持组合式 API 和选项式 API两种不同的代码风格。pinia的选项式API的编码风格与vuex非常类似,属于同根异枝,所以上手难度较低;而组合式API比较符合vue3的编程思想;
- pinia支持
TypeScript。拥有更好的类型推导和代码提示,让代码更加可靠; - pinia支持添加插件功能。pinia提供更多底层的api所有可以实现自定义插件扩展仓库功能,例如实现数据持久化存储;
- pinia相比于vuex去掉了冗余的
mutation,保留state、getters、actions。 actions支持同步和异步修改state。这样简化了操作逻辑,更好的规范和管理数据的变化;- pinia不再有嵌套结构的模块。每一个store仓库设计上是一个扁平的独立的结构,但仍然能够在 Store 之间进行交叉组合使用;
pinia官网文档:pinia.vuejs.org/zh/introduc… ,在文档中说过这样一句话:
Pinia 起源于一次探索 Vuex 下一个迭代的实验,因此结合了 Vuex 5 核心团队讨论中的许多想法。最后,我们意识到 Pinia 已经实现了我们在 Vuex 5 中想要的大部分功能,所以决定将其作为新的推荐方案来代替 Vuex。
基于上面的种种原因,因此跟上时代的步伐学习pinia。
使用pinpa
pinpa的使用有两种方式组合式 API 和选项式 API因此可以支持vue2和vue3,下面将以vue3举例说明如何使用。
创建vue3项目,简单运行下 npm i 、npm run dev
运行后将count 修改为pinia数据仓库进行控制的一个小案例。
下载依赖
npm install pinia
引入依赖
在vue项目的main.js文件中import导入依赖import { createPinia } from "pinia";,创建 pinia 实例const pinia = createPinia();将 Pinia 状态管理库集成到应用程序中.use(pinia)。
import { createApp } from "vue";
import "./style.css";
import App from "./App.vue";
//添加pinia
import { createPinia } from "pinia";
// 创建 pinia 实例
const pinia = createPinia();
//将pinia状态管理库通过use添加集成到应用程序中
createApp(App).use(pinia).mount("#app");
修改app.vue页面代码,如下图:
<script setup>
</script>
<template>
<div>这个是pinia传入的Count</div>
<div style="display: flex;justify-content: center;align-items: center;">
<button>-</button>
<div style="margin: 0 20px;">0</div>
<button>+</button>
</div>
</template>
<style scoped>
</style>
定义创建store仓库
在项目中创建store文件夹存放 pinia仓库,导入import { defineStore } from 'pinia',defineStore创建仓库。
defineStore的option API 风格参数有两个,第一个表示仓库id(唯一且不变),第二个对象(也可以省略第一个id,将仓库id写在对象中,如下面第二种方式)。
defineStore的composition API风格参数有三个,第一个表示仓库id(唯一且不变),第二个函数方法,在方法中的使用如同vue3,需要注意,创建state 通过定义变量将其变更响应式ref或reactive,创建getters 通过计算属性computed进行模拟,创建actions,写function函数,最后再将参数抛出,第三个参数(选填)属于辅助函数,一般用于插件相关,文章后面有讲解,如案例三。
option API 风格仓库
import { defineStore } from 'pinia'
/**
*
* @returns
*/
export const useCountStore = defineStore('countStore', {
state: () => ({
count: 0,
name: 'option AIP'
}),
getters: {
getCount() {
return this.count;
}
},
actions: {
increment() {
this.count++;
},
deleteCount() {
this.count--;
}
},
})
// 第二种方式
export const useCountStore2 = defineStore({
id: 'countStore2',
state: () => ({
count: 0,
name: 'option AIP2'
}),
getters: {
getCount() {
return this.count ;
}
},
actions: {
increment() {
this.count++;
},
deleteCount() {
this.count--;
}
},
})
composition API 风格仓库
import { defineStore } from 'pinia'
import { reactive,computed } from 'vue';
// 第三种方式
export const useCountStore3 = defineStore('countStore3',()=>{
// 定义state
const state = reactive({
count: 0,
name: 'compostion AIP'
})
// 定义getters
const getCount = computed(()=>{
return state.count;
})
// 定义actions
function increment() {
state.count++;
}
async function deleteCount() {
state.count--;
}
return {
state,
getCount,
increment,
deleteCount
}
})
业务代码中使用
composition API 使用方式
从 Pinia 库中导入了 storeToRefs 函数。这个函数的作用是用于将 store 中的响应式状态转换为 ref,以便在 Vue 组件的模板中可以直接使用 .value 来访问或修改它们,使得数据进行结构后依然保留响应式。
<script setup>
//引入仓库
import {useCountStore3} from "./store/useCountStore";
//通过storeToRefs函数使得,数据不失去响应式
import { storeToRefs } from "pinia";
const store = useCountStore3();
//使用 storeToRefs 函数从 store 实例中提取响应式 ref。state ref 包含 store 的状态对象,而 getCount ref 是一个函数,用于获取 store 的 count 属性
const { state,getCount } = storeToRefs(store);
//从 store 实例中提取的 store action(突变函数)。这些函数用于修改 store 的状态。
const { increment,deleteCount } = store
</script>
<template>
<div>这个是pinia传入的Count</div>
<div style="display: flex;justify-content: center;align-items: center;">
<button @click="deleteCount">-</button>
<div style="margin: 0 20px;">{{ getCount }}</div>
<button @click="increment">+</button>
</div>
</template>
option API 使用方式
使用optionAPI风格一般针对于vue2写法,pinia提供两个方法mapState, mapActions通过这两个方法可以将数据解构在computed和methods中,实现vue2调用pinia。
通过生成一个对象,并传递至组件的
computed字段, 以允许在不使用组合式 API(setup())的情况下使用一个 store 的 state 和 getter。 该对象的值是 state 属性/getter, 而键是生成的计算属性名称。 你也可以选择传递一个自定义函数,该函数将接收 store 作为其第一个参数。 注意,虽然它可以通过this访问组件实例,但它没有标注类型。
▸ mapActions 通过生成一个传递到组件的
methods字段的对象, 允许直接使用 store 的 action,而不需要使用组合式 API(setup())。 该对象的值是 action, 而键是产生的方法名称。
mapActions文档地址:pinia.vuejs.org/zh/api/modu…
mapState文档地址:pinia.vuejs.org/zh/api/modu…
<script>
import { useCountStore } from "./store/useCountStore";
import { mapState, mapActions } from "pinia";
export default {
computed: {
...mapState(useCountStore, ["state", "getCount"]),
},
methods: {
...mapActions(useCountStore, ["increment", "deleteCount"]),
},
}
</script>
<template>
<div>这个是pinia传入的Count</div>
<div style="display: flex;justify-content: center;align-items: center;">
<button @click="deleteCount">-</button>
<div style="margin: 0 20px;">{{ getCount }}</div>
<button @click="increment">+</button>
</div>
</template>
最终效果
添加插件
在 Pinia 中,我们可以为仓库添加插件,通过添加插件能够扩展以下的内容:
- 为 store 添加新的属性
- 定义 store 时增加新的选项
- 为 store 增加新的方法
- 包装现有的方法
- 改变甚至取消 action
- 实现副作用,如本地存储
- 仅应用插件于特定 store
自定义插件
下面将接着上面的计数例子,通过插件添加一段文字和实现数据存储localStorage中:
1、在src文件夹中创建plugins文件夹,并创建文件piniaPlugins.js,在文件中写以下代码:
export function myPiniaPlugin1(context){
console.log(context);
return {
text : "pinia插件"
}
}
/**
* 重置仓库状态
*/
export function myPiniaPlugin2({ store }) {
// 只有countStore3仓库才会执行
if( store.$id != "countStore3" ) return ;
// 我们首先可以将初始状态深拷贝一份
const state = deepClone(store.$state);
//定义reset方法,调用该方法实现重置仓库
store.reset = () => {
//通过$patch修改store的数据
store.$patch(deepClone(state));
};
}
/**
* 实现数据深拷贝
*/
function deepClone(obj) {
if (typeof obj !== "object") return;
const isArray = Array.isArray(obj);
let cloneObj = isArray ? [] : {};
for (let key in obj) {
// 判断是否为对象,如果是则递归深拷贝
if (obj[key] && typeof obj[key] === "object") {
cloneObj[key] = deepClone(obj[key]);
} else {
cloneObj[key] = obj[key];
}
}
return cloneObj;
}
上面代码中定义两个插件,一个是myPiniaPlugin1 往所有仓库添加text字段,一个是myPiniaPlugin2只针对于仓库id=countStore3 添加一个reset方法,调用该方法实现重置仓库数据。
修改main.js引入myPiniaPlugin1,myPiniaPlugin2 插件是通过 pinia.use() 添加到 pinia 实例的。
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import { createPinia } from 'pinia';
import { myPiniaPlugin1,myPiniaPlugin2 } from "./plugins/piniaPlugins.js"
const pinia = createPinia();
pinia.use(myPiniaPlugin1);
pinia.use(myPiniaPlugin2);
createApp(App).use(pinia).mount('#app')
运行下代码可以在控制台看到添加的数据和方法
修改app.vue文件
添加重置按钮和添加div显示插件传入的text
<script setup>
import {useCountStore3} from "./store/useCountStore";
import { storeToRefs } from "pinia";
const store = useCountStore3();
const { state,getCount } = storeToRefs(store);
//添加插件引入的方法和变量reset,text
const { increment,deleteCount,reset,text } = store
</script>
<template>
<div>{{text}}</div>
<div>这个是pinia传入的Count</div>
<div style="display: flex;justify-content: center;align-items: center;">
<button @click="deleteCount">-</button>
<div style="margin: 0 20px;">{{ getCount }}</div>
<button @click="increment">+</button>
</div>
<div>
<button @click="reset">重置</button>
</div>
</template>
最终效果
第三方插件
除了自定义插件更多是使用第三方开发的插件例如实现数据持久化存储,即刷新页面数据也回填,这里找第三方的数据持久化的教程 Pinia持久化插件:详解Pinia持久化插件pinia-plugin-persist在Vue3中的应用与实践-51CTO.COM