Vue的状态管理——Pinia

一、Pinia是什么?

Pinia 是 Vue 的专属状态管理库,它允许你跨组件或页面共享状态。

二、什么是状态?

程序是处理数据的,数据是信息的载体,比如颜色是红色或蓝色这就是数据。

状态数据的变化,比如颜色是红色或蓝色是数据,而颜色从红色变为蓝色这就是状态了。

状态的改变对应着视图的渲染或者某段逻辑的执行。比如颜色从红色变为蓝色可能就要重新渲染视图,并且执行发送请求到服务端的逻辑。

通过视图交互或者其他方式触发状态的变化,状态变化联动视图的渲染和逻辑的执行,这就是前端应用的核心。

三、状态管理

  • 什么是状态管理?

    • 状态管理是指在应用程序中管理数据(状态)的方式。在前端开发中,“状态”指的是应用在特定时间点的数据快照。

    • 数据快照包括:用户数据(登录信息、个人资料等);UI状态(加载状态、弹窗显示状态等);应用数据(商品列表、购物车内容等);服务器响应数据;路由信息等等。

    • 状态管理具体有两层含义:

      • 状态变化之前的逻辑,一般是异步的。

      • 状态变化之后的联动处理,比如渲染视图或执行某段逻辑。

  • 为什么需要状态管理?

    • 共享状态的需求:比如一份数据有多个组件需要使用,在之前我们需要用父子组件通信这样麻烦的方法,但如果使用Pinia我们只需要把这些需要共享的数据扔进一个数据仓库,在需要使用这些共享数据的组件中导入这个数据仓库就可以了

    • 保持状态一致性:当多个组件依赖于相同的状态时,状态管理确保所有组件看到的都是同一个状态,避免数据不一致的问题。

四、Pinia的三个核心概念

在组合式API下

1、State(状态)

定义state 是通过 refreactive 定义的响应式数据。它用于存储应用的状态。

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实现计数器案例

步骤:

  1. 定义Store

  2. 组件使用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;


Vue的状态管理——Pinia
http://localhost:8090//archives/vuede-zhuang-tai-guan-li-ku----pinia
作者
文希希
发布于
2025年03月15日
许可协议