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 教程 【作者:千锋教育】


相关推荐
RuoyiOffice18 小时前
Vue3+Gantt:把任务排期、依赖关系和里程碑放进项目管理
项目管理·vue3·springboot·dhtmlx-gantt·wbs·gantt·ruoyi office
AI开源软件3 天前
SpringBoot3+Vue3 财务管理系统如何设计:从业务单据到总账的模块划分
spring boot·vue3·领域建模·会计凭证·ruoyi office·业财协同·财务系统设计
Nymph_Zhu7 天前
vue3+element-plus 文件上传
前端·typescript·vue3·element-plus
Nymph_Zhu7 天前
pdf转换markdown预览及内容互相点击定位
前端·pdf·vue3·markdown·element-plus
AI源码10 天前
巨峰影视影院
vue3
RuoyiOffice10 天前
UniApp企业办公:把审批、客户和项目装进手机,能做些什么?
spring boot·vue3·uniapp·crm·flowable·移动办公·企业应用
mewowaBc11 天前
使用Pinia持久化存储实现用户认证系统
vue·pinia·用户认证系统
RuoyiOffice11 天前
SpringBoot OA推荐:面向信创选型,看看这套企业协同平台
spring boot·vue3·信创·flowable·企业协同·ruoyi office·oa选型
周厉11 天前
WebSocket 使用场景与实现方法:Java + Vue3 全栈实战
java·websocket·vue3·websocket双向通讯
红红谈说12 天前
同一套产品两个技术栈,双版本前端工程差异怎么收敛?
前端·uni-app·vue3·前后端分离·双版本维护·接口约定