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 导入进来再实例化就可以使用了。

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

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

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


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


参考资料:

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


相关推荐
RuoyiOffice1 天前
SpringBoot3+Vue3 招聘 Offer:发出、审批通过、转入职怎么走完
spring boot·vue3·offer·入职·spring boot 3·hrm·招聘管理
RuoyiOffice7 天前
SpringBoot3+Vue3 最推荐的开源 OA 系统 2026:流程驱动、资源闭环、多端互通
spring boot·vue3·flowable·oa·协同办公·spring boot 3·开源oa
鱼与宇8 天前
Element-plus离线访问(适用内网)
elementui·vue3
我就是DaLing呀!10 天前
vue3 + 独立的数据管理 Store实现视频剪辑功能
前端·typescript·vue3·canvas·store
我才是银古12 天前
当 360° 全景遇上真实地图:Vue 3 实现全景-地图双向联动的工程实践
3d·vue3·360影像
XLYcmy14 天前
HTML/CSS/JS 基础与 Vue 技术栈深度解析
java·前端·css·html·vue3·vue2·api
码视野17 天前
基于 Spring Boot + Vue3 的【大学英语四六级 (CET-4/6) 作文智能评分与句式润色系统】设计与实现(含PRD/三端高保真源码/大屏)
java·前端·人工智能·spring boot·后端·vue3
码视野17 天前
基于 Spring Boot + Vue3 的【高校化学实验室安全准入考试与危化品配伍排查系统】设计与实现(含PRD/三端高保真源码/大屏)
前端·人工智能·spring boot·后端·安全·vue3
fxshy17 天前
WebGIS 游戏化实践:基于 Cesium + Vue3 实现全球飞行模拟系统:从球体坐标、飞行动力学到地形碰撞实战
游戏·vue3·cesium·webgis·飞行模拟
jonyleek18 天前
技术实践:基于 Vue3 + Spring Cloud 微服务实现私有化文档系统的类 SaaS 协同体验
微服务·私有化部署·vue3·springcloud·协同编辑·jvs企业文档·无忧企业文档