Vue3状态管理库Pinia——核心概念(Store、State、Getter、Action)

个人简介

👀个人主页: 前端杂货铺

🙋‍♂️学习方向: 主攻前端方向,正逐渐往全干发展

📃个人状态: 研发工程师,现效力于中国工业软件事业

🚀人生格言: 积跬步至千里,积小流成江海

🥇推荐学习:🍍前端面试宝典 🍉Vue2 🍋Vue3 🍓Vue2/3项目实战 🥝Node.js🍒Three.js 🍖JS版算法

🌕个人推广:每篇文章最下方都有加入方式,旨在交流学习&资源分享,快加入进来吧

Pinia

内容 参考链接
Vue3正式发布那么久了,你认识Pinia了吗? 认识Pinia,搭建开发环境

文章目录


✨✨前言

各位新朋友、老朋友们大家好,这里是前端杂货铺,欢迎各位的到来!!

上篇文章 我们初步认识了 Pinia,并进行了项目环境的搭建。接下来,我们从 Pinia 的核心模块着手,继续学习吧~

✨✨Store

store 就是 数据仓库 ,我们可以把它看成一个 只存放数据的公共组件。

🎈🎈🎈State

state 是 store 的核心,表示当前模块的状态,里面存放着我们需要使用的数据。

我们在 store 文件夹下新建 user.ts 文件,导入 defineStore(用于定义 Store)。一般 const 定义的常量都为 useXxxStore,其中 Xxx 是 defineStore 的第一个参数 xxx。

之后我们在 state 中定义 age 属性。

.store/user.ts

typescript 复制代码
import { defineStore } from "pinia";

/**
 * pinia 中使用 definStore 方法来定义 store
 * 第一个参数是应用程序中的 store 的唯一 id
 * 第二个参数是一个对象,store 的配置项,比如配置 store 内的数据
 */
export const useUserStore = defineStore('user', {
    // state 为 store 的核心,表示当前模块的状态
    // 为了完整类型推理,推荐使用箭头函数
    state: () => {
        return {
            age: 22
        }
    }
})

在 App.vue 组件中,我们导入刚才定义的 useUserStore,并定义 userStore 常量接收 useUserStore 定义模块的相关状态,之后就可以在模板中通过 userStore.xxx 的方式进行响应式获取了。

我们可以如下定义 add() 方法,使得 userStore.age += 1,在模板中的 <button></button> 中调用 add() 方法,便可以实现每点击一次按钮加一。

App.vue

typescript 复制代码
<template>
  <div class="text">
    Hello pinia,I am {{ userStore.age }} years old.
  </div>
  <button @click="add">+1</button>
</template>

<script setup lang="ts">
import { useUserStore } from './store/user'

// 使用 useUserStore 并返回定义模块的相关状态以及方法
const userStore = useUserStore()

const add = () => {
  // userStore 模块里面的 state 是响应式数据
  userStore.age += 1
}

</script>

<style>
.text {
  background: orange;
}
</style>


🎈🎈🎈Getters

Getter 是 defineStore 参数配置项(State、Getter、Action)里面的一个属性。Getter 属性是一个对象,该对象里面是各种方法。我们可以把 Getter 看成 Vue 中的计算属性,它的作用就是返回一个新的结果。

注:与计算属性类似,Getter 也有缓存效果。(值会基于其响应式依赖被缓存。值仅会在其响应式依赖更新时才重新计算。)

在 defineStore 的第二个参数(对象)中,我们定义 getters,在里面我们可以定义一些计算方法:

  • 可以直接写箭头函数,把 state 中的值传进来,处理之后并返回
  • 可以在计算方法中调用其他计算方法,但这时要使用普通函数的写法,注:函数要加上类型限制,不然报错
  • 不同于 Vue 的计算属性,pinia 中的计算方法可以直接接收参数,注意参数写在 return 返回的函数 () 中

.store/user.ts

typescript 复制代码
import { defineStore } from "pinia";

/**
 * pinia 中使用 definStore 方法来定义 store
 * 第一个参数是应用程序中的 store 的唯一 id
 * 第二个参数是一个对象,store 的配置项,比如配置 store 内的数据
 */
export const useUserStore = defineStore('user', {
    // state 为 store 的核心
    state: () => {
        return {
            name: '前端杂货铺',
            age: 20
        }
    },
    getters: {
        /**
         * 箭头函数的写法,直接把 state 中的值传进来使用
         * @param state 
         * @returns 
         */
        doubleAge: (state) => state.age * 2,
        /**
         * 如果要在 Getter 中调用其他的计算属性方法,不能使用箭头函数
         * 注意:需要自己定义当前方法的返回值类型
         * @returns 
         */
        getNameAndAge():string {
            return this.name + " and " + this.doubleAge;
        },
        /**
         * 接收页面上传过来的参数,我们用年龄加上它并返回
         * @returns 
         */
        getAddAge() {
            return (num: number) => this.age + num;
        }
    },
})

我们通过 useUserStore() 创建 userStore 实例,就可以通过 X.x 的方式使用了。

下面的代码中,我们不仅仅练习了计算属性的多种用法,还使用了 ref 作对比。从页面显示的结果中我们可以发现,当点击 Getter +1 按钮时,和计算属性相关的数据会发生改变(前四个会变) 。当点击 Ref +1 时,和计算属性相关的数据不会发生改变(仅最后一个会变),进一步说明了 Getter 的缓存性。

App.vue

typescript 复制代码
<template>
  <div style="background: orange">
    ##state## Hello pinia,I am {{ userStore.age }} years old.
  </div>
  <div style="background: skyblue">
    ##getters箭头函数## Hello pinia,I am {{ userStore.doubleAge }} years old.
  </div>
  <div style="background: yellow">
    ##getters普通函数##,Hello pinia,I am {{ userStore.getNameAndAge }} years
    old.
  </div>
  <div style="background: yellowgreen">
    ##getters计算属性传递参数##,Hello pinia,I am
    {{ userStore.getAddAge(5) }} years old.
  </div>
  <button @click="add">##Getter## +1</button>
  <div style="background: pink">##ref响应数据##,num: {{ num }}</div>
  <button @click="numAdd">##Ref## +1</button>
</template>

<script setup lang="ts">
import { ref } from "vue";
import { useUserStore } from "./store/user";

// 使用 useUserStore 并返回定义模块的相关状态以及方法
const userStore = useUserStore();

const add = () => {
  // userStore 模块里面的 state 是响应式数据
  userStore.age += 1;
};

// ref 响应式数据
const num = ref(1);
const numAdd = () => {
  num.value += 1;
};
</script>




🎈🎈🎈Action

前面提到的 State 和 Getter 属性都主要是 数据层面的 ,并没有具体的业务逻辑代码,它们两个就和我们组件代码中的 data 数据和 computed 计算属性一样。如果我们有 业务代码 的话,最好写在 Actions 中,该属性就和我们组件代码中的 methods 相似,用来放置一些处理业务逻辑的代码。

  • state 对应 data
  • getter 对应 computed
  • action 对应 method

在 actions 中,我们定义修改 name 的同步方法 saveName() 和 异步方法 savaName2(),用于在 App.vue 组件中使用。

./store/user.ts

typescript 复制代码
import { defineStore } from "pinia";

// 异步方法,延迟 1s
const dely = () => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(100)
        }, 1000)
    })
}

/**
 * pinia 中使用 definStore 方法来定义 store
 * 第一个参数是应用程序中的 store 的唯一 id
 * 第二个参数是一个对象,store 的配置项,比如配置 store 内的数据
 */
export const useUserStore = defineStore('user', {
    // state 为 store 的核心
    state: () => {
        return {
            name: '前端杂货铺',
            age: 20
        }
    },
    /**
     * 当前模块的相关方法
     * 存放当前模块的相关业务逻辑函数
     */
    actions: {
        /**
         * 在 pinia 中,可以直接通过 actions 修改 state
         * pinia 的 actions 里面既可以是同步也可以是异步
         * @param name 
         */
        saveName(name: string) {
            this.name = name;
        },
        /**
         * 调用异步方法,延迟 1s 更新 name 的值
         * @param name 
         */
        async saveName2(name: string) {
            // 延迟 1s 更新
            await dely();
            this.name = name;
        },
    }
})

App.vue

updateName 调用 saveName() 同步方法,点击名字立即由 前端杂货铺 => 3号杂货铺 。

updateName2 调用 saveName2() 异步方法,点击名字延迟 1s 后由 前端杂货铺 => 5号杂货铺。

typescript 复制代码
<template>
  <div style="background: orange">
    Hello pinia,My name is {{ userStore.name }} .
  </div>
  <button @click="updateName">同步修改</button>
  <button @click="updateName2">异步修改</button>
</template>

<script setup lang="ts">
import { useUserStore } from "./store/user";

// 使用 useUserStore 并返回定义模块的相关状态以及方法
const userStore = useUserStore();

const updateName = () => {
  // 直接使用 userStore 调用 actions 里定义的方法
  userStore.saveName('3号杂货铺')
}

const updateName2 = () => {
  userStore.saveName2('5号杂货铺')
}
</script>




🎉🎉本篇小结

本篇文章介绍了 Pinia 的核心概念。

store 是我们的数据仓库,在这里存放的数据可以在任意组件中使用,我们只需要在组件中 import 导入进来再实例化就可以使用了。

state 是 store 的核心,在此存放我们需要的数据。state 类似于组件中的 data。

getter 等同于 store 的 state 的计算值,大多数情况下,getter 仅依赖于 state,不过它们也可能会使用其他 getter,当我们使用常规的函数定义 getter 时,可以通过 this 访问到整个 store 实例。getter 类似于组件中的 computed。

不同于 state 和 getter 针对的数据层面,action 针对的是业务逻辑层面,在 actions 中我们可以定义同步的方法,也可以定义异步的方法。actions 相当于组件中的 methods。


好啦,本篇文章到这里就要和大家说再见啦,祝你这篇文章阅读愉快,你下篇文章的阅读愉快留着我下篇文章再祝!


参考资料:

  1. Pinia 官方文档
  2. Pinia 教程 【作者:千锋教育】


相关推荐
RuoyiOffice8 小时前
SpringBoot3+Vue3 企业 AI 工作流:Tinyflow 画布、LLM 节点与在线试跑怎么落地
spring boot·大模型·vue3·agent·ai工作流·ruoyi office·tinyflow
RuoyiOffice10 小时前
Springboot + Vue3 + FormCreate表单引擎,直接用AI对话进行单据设计,告别拖拽画表单
spring boot·低代码·vue3·sse·formcreate·ruoyi office·ai表单
RuoyiOffice3 天前
SpringBoot+Vue3+Onlyoffice 企业文件生命周期:上传、业务关联、权限、版本与安全清理
spring boot·vue3·文件管理·对象存储·权限控制·ruoyi office·附件管理
格数致用6 天前
03- Luckysheet与Vue3+Electron集成实战指南
electron·vue3·luckysheet
陪我一起学编程12 天前
vue3笔记
前端·webpack·vue3·vite·脚手架·vue3笔记·组件式
Json____15 天前
家居装修 AI 智能咨询助手:让装修咨询从“大海捞针“变成“一问即答“
java·后端·vue3·it学习·wwwoop.com
liangshanbo121516 天前
面试题:什么是 Reflect?它解决了什么问题?
vue3·reflect
RuoyiOffice16 天前
SpringBoot3+Vue3 低代码业务表单:从拖拽设计、独立建表到审批与数据查询
spring boot·vue3·数据建模·flowable·版本管理·低代码表单·ruoyi office
RuoyiOffice16 天前
SpringBoot3 企业薪酬核算:考勤、绩效与薪资规则如何算出一张可解释的工资单
spring boot·vue3·规则引擎·hrm·ruoyi office·薪酬核算·薪资试算
RuoyiOffice22 天前
2026最推荐基于SpringBoot+Vue3的项目管理经营一体化:从商机、合同、任务与工时走到回款和利润
spring boot·项目管理·vue3·crm·合同管理·ruoyi office·项目经营