前言:
在现代 Web 开发中,状态管理是一个非常重要的问题。虽然 Vue.js 框架内置了 Vuex 状态管理库,但随着应用程序规模和复杂性的不断增加,Vuex 有时候也会遇到一些难以解决的问题,比如代码冗长、维护困难等。为了解决这些问题,社区中不断涌现出新的状态管理库,其中 Pinia 就是一个备受欢迎的选择。
Pinia 是一个现代化的基于 Vue.js 的状态管理库,它与 Vuex 的差异在于其代码更加简洁、易于维护,同时更好地支持 Typescript 和 SSR。此外,Pinia 还具有单独的 Store 实例和可插拔的插件体系,使得开发者能够更好地控制状态管理逻辑流程,并在应用程序不断变化时保持灵活性和可扩展性。本篇文章将从 Pinia 库的基础架构、核心概念、特点和应用场景等方面对这一库进行全面介绍,希望能够帮助读者深入理解 Pinia 的设计思想和工作原理,并在实际的开发环境中运用到实践当中。
官网地址如下:
Pinia的诞生
Pinia从2019年11月左右,开始尝试重新使用Composition API设计Vue Store。Pinia 试图尽可能接近 Vuex 的理念,旨在测试 Vuex 下一次选代的一个方案。目前,Vuex 5的open RFC,AP与Pinia的API非常相似,这说明pinia成功了。
注意,Pinia的作者(Eduardo),也是Vuejs核心团队的一员,积极参与 Router 和 Vuex 等AP的设计。设计 Pinia 的目的是想重新设计使用store的体验,同时保持Vue的容易理解的理念。将Pinia的API与Vuex保持尽可能的接近因为它不断向前发展,使人们能够方便地迁移到Vuex,甚至在未来融合两个项目。
Pinia 特性
Pinia具有以下几点特性
- 直观,像定义components一样地定义 store
- 完整的Typescript支持
- 去除 mutations,只有 state,getters,actions
- actions支持同步和异步
- Vue Devtools支持Pinia,提供更好的开发体验
- 能够构建多个 stores ,并实现自动地代码拆分
- 极其轻量 (1kb),甚至感觉不到它的存在
- Pinia 支持插件扩展自身功能
- 支持服务端渲染
Pinia 是 vue 的存储库,它允许您跨组件/页面共享状态。
Pinia 基础使用
1. 安装Pinia命令:
yarn add pinia
# 或者使用
npmnpm install pinia
创建一个根存储传递给应用程序(创建一个存储数据的数据桶,放到应用程序中去)
修改 main.js ,引入 pinia 提供的 createpinia 方法,创建根存储。
// main.ts
import { createApp } from "vue";
import App from "./App.vue";
import { createPinia } from "pinia";
const pinia = createPinia();
const app = createApp(App);
app.use(pinia);
app.mount("#app");
2.创建store
store 数据仓库 类似一个公共组件但只存放数据,这些数据其它所有的组件都能够访问且可以修改。
使用 pinia 提供的 definestore() 方法来创建一个 store,该 store 用来存放需要全局使用的数据。
在项目 src 目录下新建 store 文件夹用来存放创建的各种 store ,然后在该目录下新建 user.ts 文件,主要用来存放与 user 相关的 store 。
/src/store/user.ts
import { definestore } from 'pinia'
// 第一个参数是应用程序中 store 的唯一 id
export const useUsersStore = defineStore('users',{
// 其它配置项
}
defineStore 函数 接收两个参数:
- name : 一个字符串,必传项,该 store 的唯一 id 。
- options : 一个对象, store 的配置项,比如配置 store 内的数据,修改数据的方法等等
可以定义任意数量的 store ,因为其实一个 store 就是一个函数,让代码扁平化了,和 vue3 的实现思想是一样的。
3.使用store
创建了一个 store 即创建了一个方法,那么在 App.vue 里面使用它,该如何使用呢?
/src/App.vue
<script setup lang="ts">
import { useUsersstore } from "../src/store/user";
const store = useUsersStore();
console.log(store);
</script>
<script setup> 可以将组件选项(如 props、data、methods 等)声明为变量,并通过 defineComponent 函数将其导出为组件选项对象。这样可以使得组件选项与模板代码更加紧密地结合在一起,从而提高了代码的可读性和可维护性。
直接引入声明的 useUsersstore 方法即可
4.添加state
store 是用来存放公共数据的,那么数据具体存在在哪里呢? 前面利用defineStore 函数创建了一个 store ,该函数第二个参数是一个 options 配置项,需要存放的数据就放在 options 对象中的 state 属性内。
假设我们往 store 添加一些任务基本数据,修改 user.ts 代码
export const useUsersStore = defineStore("users", {
state:() => {
return {
name : "小猪课堂",
age: 25,
sex : "男"
};
},
});
上段代码中即给配置项添加了 state 属性,该属性就是用来存储数据的,同时往 state 中添加了3 条数据。⭕注意: state 接收的是一个箭头函数返回的值,它不能直接接收一个对象。
5.操作state
①读取 state 数据
往 store 存储数据的目的就是为了操作它,具体操作如下:
<template>
<img alt="Vue logo" src="./assets/logo.png" />
<p>姓名: {{ name }}</p>
<p>年龄: {{ age }}</p>
<p>性别: {{ sex }}</p>
</template>
<script setup lang="ts">
import { ref } from import "vue";
import { useUsersStore } from "../src/store/user";
const store = useUsersStore();
const name = ref<string>(store.name);
const age = ref<number>(store.age);
const sex = ref<string>(store.sex);
</script>
解构代码如下:
import { useUsersStore } from "../src/store/user";
const store = useUsersStore();
const { name, age, sex } = store;
②多个组件使用state
使用 store 的最重要的目的就是为了组件之间共享数据。例:新建一个child.vue 组件,在该组件内部也使用 state 数据。
<template>
<h1>我是child组件</h1>
<p>姓名: {[ name }]</p
<p>年龄:{{ age }}</p>
<p>性别: {[sex }}</p>
</template>
<script setup lang="ts">
import { useUsersstore } from "../src/store/user";
const store = useUsersstore();
const { name, age, sex } = store;
</script>
③修改state数据
想要修改 store 的数据,可以直接重新赋值即可。
import { storeToRefs } from 'pinia';
const store = useUsersStore();
const { name, age, sex } = storeToRefs(store);
利用 pinia 的 storeToRefs函数,将 state 中的数据变为了响应式的。
④重置state
利用 stare 的 $reset( )方法将 state 数据还原,例:添加重置按钮
<button @click="reset">重置store</button>
// 重置store
const reset = () =>{
store.$reset();
}
⑤批量更改state数据
用 store 的 $patch 方法,修改 app.vue代码,添加一个批量更改数据的方法
<button @click="patchstore">批量修改数据</button>
// 批量修改数据
const patchstore = () => {
store.$patch({
name:"张三",
age: 100,
sex:"女",
});
};
⑥直接替换整个state
使用 store 的 $state 方法直接替换整个 state 对象
store.$state = { counter:666, name:'张三' }
6.getters属性
getters 是 defineStore 参数配置项里面的另一个属性,getter属性值是一个对象,该对象里面是各种各样的方法。和 Vue 中的计算属性 computed 类似,作用就是返回一个新的结果。
①添加 getter
export const useUsersStore = defineStore("users", {
state: () => [
return {
name:"小猪课堂"
age:25,
sex:"男",
};
},
getters: {
getAddAge: (state) => {
return state.age + 100;
},
},
});
在配置项参数中添加了 getter 属性,该属性对象中定义了一个 getAddAge 方法该方法会默认接收一个 state 参数,也就是 state 对象,然后该方法返回的是一个新的数据。
②使用getter
组件中使用getter
<template>
<p>新年龄: [{ store.getAddAge }}</p>
<button @click="patchstore">批量修改数据</button>
</template>
<script setup lang="ts">
import { useUsersStore } from "../src/store/user"
const store = useUsersStore();
// 批量修改数据
const patchstore = () => {
store.$patch({
name:"张三"
age: 100,
sex:"女"
});
};
</script>
③ getter中调用其它getter
在这一个getter 方法中调用其它 getter 方法,这个时候如何调用呢?
直接在 getter 方法中调用 this,this 指向的便是 store 实例,所以理所当然的能够调用到其它 getter 。(不能使用箭头函数)
7.actions属性
①添加action
state 和 getters 属性都主要是数据层面的,并没有具体的业务逻辑代码,与们组件代码中的 data 数据和 computed 计算属性一样。
而卸载 actions 属性与 methods 相似,用来放置一些处理业务逻辑的方法。
actions 属性值同样是一个对象,该对象里面也是存储的各种各样的方法,包括同步方法和异步方法。
export const useUsersStore = defineStore("users",{
state: () => {
return {
name:"小猪课堂" :
age:25,
sex :"男",
};
},
getters: {
getAddAge: (state) => {
return (num: number) => {
return (num:muber) => state.age + num;
},
getNameAndAge(): string {
return this.name + this.getAddAge; // 调用其它getter
},
},
actions :{
saveName(name : string){
this .name = name ;
},
},
});
在实际场景中,该方法可以是任何逻辑,比方法当作一个普通的方法即可,特殊之处在于该如发送请求、存储 token 等等。可以把 actions方法内部的 this 指向的是当前 store。
②使用action
const saveName {
store.saveName("我是小猪");
}
总结:
Pinia 是一种轻量级、用于 Vue.js 应用程序的状态管理库, 它可以帮助开发人员更好地组织和管理应用程序状态。与其他状态管理库相比,Pinia 具有更快的速度和更小的体积,同时具有可扩展性和易用性等优点。
使用 Pinia 可以轻松地实现状态管理,并对应用程序中的状态进行更好的组织和维护,从而提高代码的可读性和可维护性。它还支持 TypeScript,并且具有良好的文档、易懂的 API 设计和友好的社区支持。