Vue的状态管理——Pinia
一、Pinia是什么?
Pinia 是 Vue 的专属状态管理库,它允许你跨组件或页面共享状态。
二、什么是状态?
程序是处理数据的,数据是信息的载体,比如颜色是红色或蓝色这就是数据。
状态是数据的变化,比如颜色是红色或蓝色是数据,而颜色从红色变为蓝色这就是状态了。
状态的改变对应着视图的渲染或者某段逻辑的执行。比如颜色从红色变为蓝色可能就要重新渲染视图,并且执行发送请求到服务端的逻辑。
通过视图交互或者其他方式触发状态的变化,状态变化联动视图的渲染和逻辑的执行,这就是前端应用的核心。
三、状态管理
什么是状态管理?
状态管理是指在应用程序中管理数据(状态)的方式。在前端开发中,“状态”指的是应用在特定时间点的数据快照。
数据快照包括:用户数据(登录信息、个人资料等);UI状态(加载状态、弹窗显示状态等);应用数据(商品列表、购物车内容等);服务器响应数据;路由信息等等。
状态管理具体有两层含义:
状态变化之前的逻辑,一般是异步的。
状态变化之后的联动处理,比如渲染视图或执行某段逻辑。
为什么需要状态管理?
共享状态的需求:比如一份数据有多个组件需要使用,在之前我们需要用父子组件通信这样麻烦的方法,但如果使用Pinia我们只需要把这些需要共享的数据扔进一个数据仓库,在需要使用这些共享数据的组件中导入这个数据仓库就可以了
保持状态一致性:当多个组件依赖于相同的状态时,状态管理确保所有组件看到的都是同一个状态,避免数据不一致的问题。
四、Pinia的三个核心概念
在组合式API下
1、State(状态)
定义:state
是通过 ref
或 reactive
定义的响应式数据。它用于存储应用的状态。
import { defineStore } from 'pinia';
import { ref } from 'vue';
export const useCounterStore = defineStore('counter', () => {
const count = ref(0);
return { count };
});
在访问state中的数据时,直接使用实例化的store . 属性
的方式访问
<p>Count: {{ counterStore.count }}</p>
修改state,可直接使用赋值的方式,或者使用$patch方法批量修改多个属性
2、Getter(计算属性)
定义:getter
是通过 computed
定义的计算属性。它用于对 state
中的数据进行加工处理。
import { defineStore } from 'pinia';
import { ref, computed } from 'vue';
export const useCounterStore = defineStore('counter', () => {
const count = ref(0);
const doubleCount = computed(() => count.value * 2);
return { count, doubleCount };
});
访问getter,在组件中,通过 store.getterProperty
的形式直接访问 getter
中的属性。
<button @click="counterStore.increment">Increment</button>
3、Action(动作)
定义:action
是定义在 store 中的函数,用于处理业务逻辑。它可以是同步的,也可以是异步的。
import { defineStore } from 'pinia';
import { ref } from 'vue';
export const useCounterStore = defineStore('counter', () => {
const count = ref(0);
function increment() {
count.value++;
}
async function fetchData() {
// 异步操作示例
const data = await api.get('/some-data');
count.value = data.count;
}
return { count, increment, fetchData };
});
访问action,在组件中,通过 store.actionMethod()
的形式调用 action
中的方法。
五、安装Pinia
npm install pinia
在main.js中导入pinia
import { createPinia } from "pinia";
import App from "./App.vue";
const app = createApp(App);
const pinia = createPinia();
app.use(pinia);
app.mount("#app");
六、使用Pinia实现计数器案例
步骤:
定义Store
组件使用Store
//定义store
import {defineStore} from 'pinia'
import {ref} form 'vue'
export const useCounterStore = defineStore('counter', () => {
//数据store
const count = ref(0)
//修改数据的方法
const increment = () => {
count.value++
}
//以对象形式返回
return {count, increment}
})
//组件使用Store
<template>
<button @click="counterStore.increment">
{{ counterStore.count }}
</button>
</template>
<script setup>
//1、导入‘useCounterStore’ 方法
import {useCounterStore} from '@/stores/counter'
//2、执行方法得到counterStore对象
const counterStore = useCounterStore()
</script>
七、getter使用
在counter.js中定义getter
//getter定义
const doublecount = computed(() => count.value * 2);
//并返回
return {
count,
doublecount,
increment,
};
打印counterStore对象
八、异步action使用
在counter.js中导入axios
//导入一个方法defineStore
import { defineStore } from "pinia";
import { ref, computed } from "vue";
import axios from "axios";
const API_URL = "http://geek.itheima.net/v1_0/channels";
export const useCounterStore = defineStore("counter", () => {
//定义数据
const count = ref(0);
//修改数据的方法
const increment = () => {
count.value++;
};
//getter定义
const doublecount = computed(() => count.value * 2);
//定义异步action
const list = ref([]);
const getList = async () => {
const res = await axios.get(API_URL);
list.value = res.data.data.channels;
};
//以对象的方式return供组件使用
return {
count,
doublecount,
increment,
list,
getList,
};
});
九、storeToRefs方法
使用storeToRefs函数可以辅助保持数据的响应式解构
<template>
<button @click="counterStore.increment">
{{ count }}</button
>{{ doublecount }}
<ul>
<li v-for="item in counterStore.list" :key="item.id">{{ item.name }}</li>
</ul>
</template>
<script setup>
//1、导入‘useCounterStore’ 方法
import { onMounted } from "vue";
import { useCounterStore } from "./stores/counter";
import { storeToRefs } from "pinia";
//2、执行方法得到counterStore对象
const counterStore = useCounterStore();
console.log(counterStore);
//直接解构赋值(会响应式丢失)
// const { count, doublecount } = counterStore;
//方法包裹(保持响应式更新)
const { count, doublecount } = storeToRefs(counterStore);
//触发action
onMounted(() => {
counterStore.getList();
});
</script>
<style lang="scss" scoped></style>
为什么用storeToRefs就可以保持响应式?
我们尝试打印一下两者的数据
//直接解构赋值(会响应式丢失)
const { count, doublecount } = counterStore;
console.log(count, doublecount);//打印结果是 0 0
//方法包裹(保持响应式更新)
const { count, doublecount } = storeToRefs(counterStore);
console.log(count, doublecount);
打印结果是两个响应式对象
如果想针对方法解构赋值,比如increment,就不能使用storeToRefs了,而是使用最初的方法
//方法直接解构赋值
const { increment } = counterStore;