当前位置:首页 » 《资源分享》 » 正文

Pinia 实战指南:轻松驾驭前端状态管理

9 人参与  2024年09月13日 19:21  分类 : 《资源分享》  评论

点击全文阅读


前言

本文讲解一下在前端开发中经常使用的一个状态管理工具Pinia

Pinia 是 Vue 的专属状态管理库,很值得我们深入去学习一下

Pinia是什么?

Pinia是专门为Vue.js应用程序设计的一个状态管理库

主要特点:

简单性: Pinia的设计目标是提高开发效率和用户体验,因此它更加简单和简洁。模块化: Pinia允许你将应用程序的状态划分为独立的"stores",每个store都有自己的状态、actions和getters。这种模块化设计使得代码更加组织有序。响应式: Pinia利用Vue.js内置的响应式系统,状态的变化会自动反映在UI上。TypeScript支持: Pinia对TypeScript有良好的支持,非常适合使用TypeScript的项目。组合式API: Pinia被设计为与Vue.js的组合式API配合使用,提供了更灵活的状态管理方式。Devtools集成: Pinia与Vue.js Devtools扩展程序集成,提供了强大的调试体验。

普通父子通信

这里我们首先看看普通的父子之间的通信是怎么样的?

父传子

Child.vue

Child.vue组件接受一个名为 title 的 prop,它是一个字符串类型。如果父组件没有传递 title prop,则默认值为 "hello"。在模板中直接渲染 title
<template>  <div>    {{ title }}  </div></template><script setup>// 父传子defineProps({  //   title: String,  title: {    type: String,    default: "hello",  },});</script>

App.vue

在App.vue组件中,引入了 Child 组件。定义了一个名为 title 的变量,并赋值为 "hello world"。在 <Child> 组件上,使用 :title="title"title 变量传递给 Child 组件的 title prop。
<template>  <Child :title="title"></Child></template><script setup>import { ref } from "vue";import Child from "./components/Child.vue";const title = "hello world";</script>

实现效果:

image.png

image.png

子传父

Child.vue

Child.vue组件包含一个按钮,当点击按钮时会触发 submit 方法。在 submit 方法中,使用 defineEmits 函数定义了一个名为 'onSub' 的自定义事件。通过 emit('onSub', success) 触发这个自定义事件,并传递一个名为 'success' 的数据。
<template>  <button @click="submit">提交</button></template><script setup>// 子传父const success = "success-666";const emit = defineEmits(['onSub']);const submit = () => {    // 把submit方法传给父组件    emit('onSub', success);}</script>

App.vue

在App.vue组件中,引入了 Child 组件。使用 @onSub="handle" 监听 Child 组件触发的 'onSub' 事件。当 'onSub' 事件被触发时,会执行 handle 函数,并将 Child 组件传递的数据 'success' 赋值给 res 变量。最后在模板中显示 res 的值
<template>  <Child @onSub="handle"></Child>  <h1>{{ res }}</h1></template><script setup>import { ref } from "vue";import Child from "./components/Child.vue";const res = ref("");const handle = (e) => {  res.value = e;};</script>

实现效果:

动画.gif

使用公共变量通信

接下来我们去使用公共变量去通信,其实这个就有一点像pinia的通信方式了,但是这不是pinia,所以还不是很优雅,不过同样能够去实现通信的效果

global.js

global.js 文件中定义了一个全局的 sum 变量,并通过 ref 函数将其包裹成一个响应式的引用

import { ref } from "vue";export const sum = ref(0);

App.vue

App.vue 中, AddCount 两个组件被引入和渲染

<template>  <Add />  <Count /></template><script setup>import Add from "./components/Add.vue";import Count from "./components/Count.vue";</script>

Add.vue

Add.vue 中, sum 变量被直接从 @/global 中导入并使用。当点击按钮时, sum 的值会递增

<template>  <div>    <button @click="sum++">add{{ sum }}</button>  </div></template><script setup>import { sum } from '@/global';</script>

Count.vue

Count.vue 中, sum 变量也被从 ../global 中导入并用于渲染

<template>  <div>    <h2>{{ sum }}</h2>  </div></template><script setup>import { sum } from '../global'</script>

这种方式确实可以实现组件之间的数据共享,但存在以下一些问题:

全局污染: 在大型应用程序中,如果大量使用这种全局变量的方式,很容易造成命名冲突和代码维护困难。缺乏可扩展性: 随着应用程序的增长,管理全局变量会变得越来越复杂。很难对状态进行模块化和分层管理。缺乏工具支持: 全局变量不像 Pinia 那样提供丰富的开发工具支持,比如时间旅行调试、持久化、热更新等。不适合 SSR: 全局变量在服务端渲染(SSR)场景下可能会出现问题,因为每个请求都会共享同一个全局状态。

Pinia 提供了一个更加结构化和可维护的状态管理解决方案,接下来我们就去使用pinia了

Pinia通信

首先我们需要去安装Pinia

npm i pinia

接下来创建Pinia

image.png

import { createPinia } from "pinia";const pinia = createPinia();export default pinia;

引入store到vue项目之中去

import { createApp } from "vue";import App from "./App3.vue";import store from "./store";const app = createApp(App);app.use(store);app.mount("#app");

接下来我们就可以去使用Pinia了,创建user.js

image.png

import { defineStore } from "pinia";export const useUserStore = defineStore({  id: "user",  // 数据源  state: () => {    return {      userInfo: {        name: "c",        age: 18,      },    };  },  // 仓库里的方法  actions: {    // 修改数据    changeUserInfo(info) {      this.userInfo = info;    },    // 修改名字    changeUserName(name) {      this.userInfo.name = name;    },    // 修改年龄    changeUserAge(age) {      this.userInfo.age = age;    },  },  // 仓库里的计算方法  getters: {    afterAge(state) {      return state.userInfo.age + 10;    },  },});

定义 Store:

使用 defineStore 函数定义了一个名为 "user" 的 store。这个 store 就是用于管理用户相关的状态和逻辑。

State:

state 函数返回一个对象,该对象表示 store 的初始状态。在这个例子中,初始状态包含一个 userInfo 对象,包含 nameage 属性。

Actions:

actions 对象定义了三个方法:

changeUserInfo: 用于更新整个 userInfo 对象。changeUserName: 用于更新 userInfo.name 属性。changeUserAge: 用于更新 userInfo.age 属性。

这些 action 方法可以在组件中调用,以改变 store 中的状态。

Getters:

getters 对象定义了一个计算属性 afterAge,它会返回 userInfo.age 加 10 的结果。这个计算属性可以在组件中使用,就像使用组件的 computed 属性一样。

接下来我们就去使用它了

首先创建User.vue

image.png

这个组件直接使用 useUserStore 获取 user store 的实例。

通过 computed 属性和 storeToRefs 工具函数,我们可以直接访问 store 中的 nameuserInfo 属性,而不需要手动解构。

在模板中,我们展示了 nameuserInfo.age 以及通过 getter 计算的 afterAge

<template>  <div>姓名{{ name }}</div>  <div>年龄{{ userInfo.age }}</div>  <div>十年后年龄{{ userStore.afterAge }}</div></template><script setup>import { useUserStore } from "@/store/user";import { computed } from "vue";import { storeToRefs } from "pinia";const userStore = useUserStore();const name = computed(() => userStore.userInfo.name);const { userInfo } = storeToRefs(userStore);</script>

接下来我去创建UpdataUser.vue

image.png

这个组件也使用了 useUserStore 获取 user store 实例。在 changeNamechangeAge 方法中,我们调用了 store 的 changeUserNamechangeUserAge action 方法来更新状态。这里需要注意,不要直接修改 userStore.userInfo 对象,而是使用定义好的 action 方法。这样可以确保状态的更新是受控的,并且能够触发相关的副作用和订阅。
<template>  <div>    <button @click="changeName">修改名字</button>    <button @click="changeAge">修改名字</button>  </div></template><script setup>import { useUserStore } from "@/store/user";const userStore = useUserStore();const changeName = () => {  // userStore.userInfo.name = "cc";// 不要写这种代码  userStore.changeUserName("cc");};const changeAge = () => {  userStore.changeUserAge(20);};</script>

App.vue

这是一个简单的父组件,包含了 UserUpdataUser 两个子组件。在这个示例中,我们演示了如何在不同的组件中使用同一个 Pinia store。
<template>  <User />  <UpdataUser/></template><script setup>import User from "./components/User.vue";import UpdataUser from './components/UpdataUser.vue'</script>

实现效果:

动画.gif

但是目前实现的效果是不具备持久化的能力,也就是说如果我们刷新页面,数据就会回到最开始的值

动画.gif

Pinia 支持开启持久化功能

将 store 的状态保存在浏览器的本地存储或会话存储中,这样即使页面刷新或浏览器关闭,状态也不会丢失。

开启 Pinia 持久化功能的步骤如下:

安装 Pinia 持久化插件:
npm install pinia-plugin-persistedstate
在 Pinia store 中引入并启用 persistedstate 插件:
import { createPinia } from 'pinia'import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'const pinia = createPinia()pinia.use(piniaPluginPersistedstate)
在 Pinia store 中添加 persist: true 选项,指定要持久化的状态:
persist: {    // 持久化    enabled: true,    strategies: [      // 持久化策略      {        paths: ["userInfo"], // 需要持久化的数据        storage: localStorage, // 持久化存储的位置      },    ],  },

现在就成功开启了持久化功能

动画.gif

可以看到我们修改数据后会在localStorage里添加数据,并且刷新页面数据也不会复原

总结

这篇文章详细介绍了如何在 Vue 组件中使用 Pinia 状态管理库。

使用 Pinia 与不使用 Pinia 时的组件通信方式相比,组件之间的通信更加简单和清晰

不再需要通过事件总线或 prop 逐层传递数据,而是可以直接访问共享的状态

这种模式可以让你的应用程序更加模块化和可维护


点击全文阅读


本文链接:http://zhangshiyu.com/post/159521.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1