(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('购买商品成功');
}

### 如何在 Pinia使用 `watch` 监听状态变化 在 Vue.js 的生态系统中,Pinia 是一种用于管理应用状态的状态管理模式和库。为了监听 Pinia store 中的状态变化,可以利用 Vue 提供的组合式 API 功能之一——`watch` 函数。 下面是一个具体的例子来展示如何设置一个 watcher 来响应 Pinia Store 中特定属性的变化: ```javascript import { defineStore } from &#39;pinia&#39;; // 定义并导出一个新的 pinia store export const useMainStore = defineStore(&#39;main&#39;, { state: () => ({ count: 0, }), }); ``` 当需要在一个组件内部监控此 store 的某个状态时,可以通过如下方式操作: ```typescript <script setup> import { watch } from &#39;vue&#39; import { useMainStore } from &#39;./stores/main&#39; const mainStore = useMainStore() watch( () => mainStore.count, (newValue, oldValue) => { console.log(`count changed from ${oldValue} to ${newValue}`) } ) </script> ``` 如果希望监听整个对象而不是单个属性,则可传递返回该对象的 getter 给 `watch` 方法,并开启深度监视选项以捕捉嵌套属性的变化: ```typescript <script setup> import { watch } from &#39;vue&#39; import { useMainStore } from &#39;./stores/main&#39; const mainStore = useMainStore() watch( () => ({ ...mainStore }), // 或者直接传入 mainStore 如果不需要解构赋值 (newState, oldState) => { console.log(&#39;state has been updated&#39;) }, { deep: true } ) </script> ``` 通过这种方式可以在任何地方轻松地跟踪来自 Pinia 存储的数据变动情况[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

緑水長流*z

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

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

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

打赏作者

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

抵扣说明:

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

余额充值