(12)Pinia——状态管理的详细使用


本系列教程目录Vue3+Element Plus全套学习笔记-目录大纲


第1章 Pinia

Pinia是Vue推出的一个状态管理库,Pinia用于替代Vuex并优化开发体验。Pinia 最初被设计为 Vuex 5 的提案,后来独立发展,成为官方推荐的状态管理工具。

Tips:Vuex 在Vue 3 时代不再积极更新,Pinia 则是更轻量的替代方案。

Pinia官网:

Pinia官网:https://pinia.vuejs.org/zh/

1.1 Pinia的概述

1.1.1 什么是状态管理

在使用Vue开发中,组件之间经常需要传值,基于父子、兄弟组件之间的传值可能会很方便,但是如果是没有关联的组件之间要使用同一组数据,这样就不得不在访问该组件的路径上携带这些参数,这样极为不便。

状态管理是指在应用程序中集中存储、管理和共享数据的机制,即建立一块区域存储所有组件共享的数据,类似于后端的session或者前端的localstorage。在前端开发中,状态(State)通常指:

  • 1)组件内部的状态(如 data()refreactive 定义的变量)。
  • 2)跨组件的共享状态(如用户登录信息、全局主题、购物车数据)。

可以看到,状态(State)就是数据,状态管理就是放在一个集中的位置对这些数据进行统一管理,包括存储、访问、修改等。状态管理的核心就是解决数据在不同组件间的共享问题

1.1.2 Pinia的核心概念

一个完整的Pinia包含三个部分,分别为Store、Getter、Actions,他们的概念如下:

  • State:表示 Pinia Store 内部保存的数据(data)。
  • Getter:可以认为是 Store 里面数据的计算属性(computed),用于获取最新的Store数据。
  • Actions:是暴露修改数据的几种方式,用于修改Store数据。

1.1.3 搭建Pinia工程

和 vue-router 一样,我们想要使用 pinia 都需要先安装它。

首先创建一个Vite工程:

npm create vite

进入Vtie工程,安装vite依赖:

npm install

安装pinia组件:

npm install pinia			# 按照pinia组件

修改mian.js,让Vue实例使用Pinia组件:

import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
// 导入 createPinia 
import {createPinia} from 'pinia'

// 创建 pinia 实例
const pinia = createPinia()

createApp(App)
    .use(pinia)             // 使用 pinia 实例
    .mount('#app')

1.2 Pinia - State

Store是存储数据的地方,我们也可以把它理解为一个公共组件,只不过该公共组件只存放数据,这些数据我们其它所有的组件都能够访问且可以修改。

1.2.1 State的基本使用

我们需要使用pinia提供的defineStore()方法来创建一个store,该store用来存放我们需要全局使用的数据。

首先在项目src目录下新建store文件夹,用来存放我们创建的各种store,然后在该目录下新建user.js文件,主要用来存放与user相关的store。

定义一个存储单元:

// 导入defineStore函数
import {defineStore} from 'pinia'

// 定义一个 user 存储单元(存储单元的名称不能与其他存储单元重名)
export const userStore = defineStore('user', {
    // 定义state状态
    state: () => {
        return {
            username: '小灰',
            money: 10000
        }
    }
});

定义一个User组件,用于展示State的数据:

<script setup>
import {userStore} from '../store/user';

// 获取 store 对象
let user = userStore();

let {username, money} = user;
</script>

<template>
  <div class="user">
    <h3>This is user Page</h3>

    <hr>
    <p>Username: {{ username }}</p>

    <hr>
    <p>Money: {{ money }}</p>

  </div>

</template>

<style scoped>
.user {
  padding: 10px 30px;
  border: 3px solid blue;
}
</style>

定义一个UserUpdate组件,用于修改State数据:

<script setup>
import {userStore} from '../store/user';

// 获取 store 对象
let user = userStore();

function changeMoney() {
  user.money += 100;
  console.log('新的余额:', user.money);
}

</script>

<template>
  <div class="userUpdate">
    <h3>This is userUpdate Page</h3>

    <div>
      <button @click="changeMoney">增加余额</button>

    </div>

  </div>

</template>

<style scoped>
.userUpdate {
  padding: 10px 30px;
  border: 3px solid green;
}
</style>

App.vue组件:

<script setup>
import User from "./components/user.vue";
import UserUpdate from "./components/userUpdate.vue";
</script>

<template>
  <div class="app">
    <h3>This is App Page</h3>

    <user />
    <user-update />
  </div>

</template>

<style>
.app {
  padding: 0 30px;
  border: 3px solid red;
}
</style>

我们发现修改user中的money属性时,并不具备响应式功能:

想要提供响应式功能我们可以使用Pinia提供的storeToRefs函数(注意,此时ref函数无法提供响应式功能):

// 无法提供响应式功能
// let username = ref(user.username)
// let money = ref(user.money)

// 使用 Pinia 提供的 storeToRefs 转换为响应式对象
let {username, money} = storeToRefs(user);

1.2.3 批量更改 state 数据

使用 store 的$patch 方法我们可以对State中的数据进行批量修改。

在userUpdate组件中添加按钮:

<button @click="patchUser">批量修改</button>

提供函数:

function patchUser() {
  user.$patch({
    money: 20000,
    username: '小黑'
  });
}

但实际上,我们通过如下代码也可以对state中的数据进行批量修改:

user.username = '小黑';
user.money = 20000;

在 Pinia 中,$patch 和直接修改 store 的属性都能达到更新状态的目的,但两种方式的底层渲染区别不一样。

  • $patch:一次性批量更新多个属性,只会触发一次订阅(如组件的重新渲染或 devtools 的更新)
  • 直接修改State:每次赋值都会触发独立的响应式更新。
user.username = '小黑'; // 触发一次更新
user.money = 20000;    // 再触发一次更新

实际上,无论是通过$patch还是直接修改State都不推荐。Pinia官方推荐使用actions中的方法修改数据,这样无论是从性能上还是语义上都符合Pinia对修改数据的定义。

1.2.3 替换 State

有时我们想要替换整个State的数据可以使用$state方法。

在userUpdate组件中添加按钮:

<button @click="replace">替换整个State</button>

提供函数:

function replaceUser() {
  // 替换整个State
  user.$state = {username: '小白', money: 30000, age: 20};
  
  // state中新增了一个age属性
  console.log('新的state:', user.$state);
}

1.2.4 重置 State

有时候我们修改了 state 数据,想要将它还原,此时,我们直接调用 store 的$reset()方法即可将 state 数据还原。但需要注意的是,替换之后的 state 本质上更改了一个新的 state,此时如果使用重置那么将会重置回新的 state。

在userUpdate组件中添加按钮:

<button @click="reset">重置store</button>

提供函数:

// 重置store
function reset() {
  user.$reset();
}

1.3 Pinia - Getter

getter属性值是一个对象,该对象里面是各种各样的方法。大家可以把getter想象成Vue中的计算属性,它的作用就是返回一个新的结果,就和computed一样。

1.3.1 Getter使用

定义Getter:

// 导入defineStore函数
import {defineStore} from 'pinia'

// 定义一个 user 存储单元(存储单元的名称不能与其他存储单元重名)
export const userStore = defineStore('user', {
    // 定义state状态
    state: () => {
        return {
            username: '小灰',
            money: 10000
        }
    },
    getters: {
        // 调用getMoney方法,返回state.money+100
        getMoney: (state) => {
            return state.money + 100;
        },
    },
});

user.vue:

<script setup>
import {userStore} from '../store/user';
import {storeToRefs} from "pinia";

// 获取 store 对象
let user = userStore();

// let {username, money} = user;

// 无法提供响应式功能
// let username = ref(user.username)
// let money = ref(user.money)

// 使用 Pinia 提供的 storeToRefs 转换为响应式对象
let {username, money} = storeToRefs(user);
</script>

<template>
  <div class="user">
    <h3>This is user Page</h3>

    <hr>
    <p>Username: {{ username }} - - - - Money: {{ money }}</p>

    <p>新余额:{{user.getMoney}}</p>

  </div>

</template>

<style scoped>
.user {
  padding: 0 30px;
  border: 3px solid blue;
}
</style>

userUpdate.vue:

import {userStore} from '../store/user';
// 获取 store 对象
let user = userStore();

function changeMoney() {
  user.money += 100;
  console.log('使用State的money属性:', user.money);
  console.log('使用Getter的getMoney方法:', user.getMoney);
}

1.3.2 Getter传参

getter函数就相当于store的计算属性,和vue的computed类似。computed是不能直接传递参数的,但我们可以通过一些办法来达到传值效果。

getter函数如果需要传值的话该函数内返回一个函数,这样在调用getter函数时相当于调用该函数内返回的那个函数。

定一个getAddMoney的Getter函数:

getAddMoney: (state) => {
    // 调用addMoney方法,返回一个函数,参数为money,返回state.money+money
    return (money) => {
        return state.money + money;
    }
}

在user.vue中使用Getter:

<p>新余额:{{user.getAddMoney(1000)}}</p>

在js中使用Getter:

console.log('使用Getter的getAddMoney方法:', user.getAddMoney(1000));

1.4 Pinia - Actions

state和getters属性都主要是数据层面的,并没有具体的业务逻辑代码,它们两个就和我们组件代码中的data数据和computed计算属性一样。那么,如果我们有业务代码的话,最好就是写在actions属性里面,actions就和我们组件代码中的methods相似,用来放置一些处理业务逻辑的方法。

actions方法用于处理业务逻辑,在实际场景中,该方法可以是任何逻辑,比如发送请求、存储token等等。我们可以把actions方法当作一个普通的方法即可,特殊之处在于该方法内部的this指向的是当前store

定义一个actions:

// 导入defineStore函数
import {defineStore} from 'pinia'

// 定义一个 user 存储单元(存储单元的名称不能与其他存储单元重名)
export const userStore = defineStore('user', {
    // 定义state状态
    state: () => {
        return {
            username: '小灰',
            money: 10000
        }
    },
    getters: {
        ....
    },
    actions: {
        pay(money) {
            // this代表当前的userStore实例
            this.money -= money;
            console.log('支付了' + money + '元');
        }
    }
});

在userUpdate.vue中添加按钮:

<button @click="pay">购买商品</button>

添加函数:

function pay() {
  // 购买商品
  user.pay(500)
  console.log('购买商品成功');
}

### Vue3 Pinia 状态管理教程 #### 什么是 PiniaPinia 是一个专门为 Vue 3 设计的状态管理库,它提供了简单而灵活的 API 来处理组件之间的共享状态。相比 Vuex,Pinia 更加现代化且易于使用[^1]。 #### 如何安装 Pinia? 要开始使用 Pinia,首先需要通过 npm 或 yarn 安装该库: ```bash npm install pinia ``` 或者: ```bash yarn add pinia ``` 接着,在项目的入口文件 `main.ts` 或 `main.js` 中引入并注册 Pinia: ```javascript import { createApp } from &#39;vue&#39;; import { createPinia } from &#39;pinia&#39;; const app = createApp(App); const pinia = createPinia(); app.use(pinia).mount(&#39;#app&#39;); ``` 这一步会将 Pinia 注册到整个应用程序中[^5]。 --- #### 创建 Store 在 Pinia 中,所有的状态都存储在一个或多个 store 中。可以通过定义函数来创建这些 store。例如: ```typescript // stores/counter.ts import { defineStore } from &#39;pinia&#39;; export const useCounterStore = defineStore(&#39;counter&#39;, { state: () => ({ count: 0, }), actions: { increment() { this.count++; }, decrement() { this.count--; }, }, }); ``` 在这个例子中,我们创建了一个名为 `counter` 的 store,其中包含了初始状态 `count` 和两个用于修改状态的方法 `increment` 和 `decrement`[^2]。 --- #### 在组件中使用 Store 一旦 store 被创建,就可以在任何组件中轻松访问它的状态和方法。以下是具体用法: ```vue <template> <div> <p>Count: {{ counter.count }}</p> <button @click="counter.increment">Increment</button> <button @click="counter.decrement">Decrement</button> </div> </template> <script setup> import { useCounterStore } from &#39;@/stores/counter&#39;; const counter = useCounterStore(); </script> ``` 在这里,`useCounterStore()` 返回了之前定义的 `counter` store 实例,从而可以方便地读取其状态以及调用相关操作[^1]。 --- #### 修改 State 的两种方式 除了通过 action 方法间接更新外,还可以直接更改 state 值。例如: ```typescript this.$patch({ count: newValue, }); // 或者一次性批量更新多项属性 this.$patch((state) => { state.count += 1; state.anotherProperty = someValue; }); ``` 这种方式允许更细粒度地控制如何改变内部数据结构[^4]。 --- #### 持久化存储 为了保持用户刷新页面后的状态不变,通常需要用到本地存储技术(如 localStorage)。借助社区插件如 [@pinia-plugin-persistedstate](https://github.com/prazdevs/pinia-plugin-persistedstate),能够快速实现这一功能: ```bash npm i @pinia-plugin-persistedstate ``` 然后将其集成至项目配置里: ```javascript import { createPinia } from &#39;pinia&#39;; import persist from &#39;@pinia-plugin-persistedstate&#39;; const pinia = createPinia().use(persist); export default pinia; ``` 现在每当某个特定字段被标记为持久化的部分时,都会自动同步保存下来[^4]。 --- #### 多模块设计 对于大型应用来说,推荐采用分层架构模式——即把不同业务逻辑拆分成独立的小型子仓库。比如购物车服务单独封装成 cart.store 文件夹下的 js/ts 文档形式存在;登录验证则归属 auth.store 下面等等。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

緑水長流*z

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值