前言
本文讲解一下在前端开发中经常使用的一个状态管理工具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,它是一个字符串类型。 - 如果父组件没有传递
titleprop,则默认值为"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组件的titleprop。
<template><Child :title="title"></Child></template><script setup>import{ ref }from"vue";import Child from"./components/Child.vue";const title ="hello world";</script>
实现效果:


子传父
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']);constsubmit=()=>{// 把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("");consthandle=(e)=>{
res.value = e;};</script>
实现效果:

使用公共变量通信
接下来我们去使用公共变量去通信,其实这个就有一点像pinia的通信方式了,但是这不是pinia,所以还不是很优雅,不过同样能够去实现通信的效果
global.js
global.js
文件中定义了一个全局的
sum
变量,并通过
ref
函数将其包裹成一个响应式的引用
import{ ref }from"vue";exportconst sum =ref(0);
App.vue
在
App.vue
中,
Add
和
Count
两个组件被引入和渲染
<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

import{ createPinia }from"pinia";const pinia =createPinia();exportdefault 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

import{ defineStore }from"pinia";exportconst 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对象,包含name和age属性。 - Actions:-
actions对象定义了三个方法:-changeUserInfo: 用于更新整个userInfo对象。-changeUserName: 用于更新userInfo.name属性。-changeUserAge: 用于更新userInfo.age属性。- 这些 action 方法可以在组件中调用,以改变 store 中的状态。 - Getters:-
getters对象定义了一个计算属性afterAge,它会返回userInfo.age加 10 的结果。- 这个计算属性可以在组件中使用,就像使用组件的 computed 属性一样。
接下来我们就去使用它了
首先创建User.vue

- 这个组件直接使用
useUserStore获取 user store 的实例。 - 通过
computed属性和storeToRefs工具函数,我们可以直接访问 store 中的name和userInfo属性,而不需要手动解构。 - 在模板中,我们展示了
name、userInfo.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

- 这个组件也使用了
useUserStore获取 user store 实例。 - 在
changeName和changeAge方法中,我们调用了 store 的changeUserName和changeUserAgeaction 方法来更新状态。 - 这里需要注意,不要直接修改
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();constchangeName=()=>{// userStore.userInfo.name = "cc";// 不要写这种代码
userStore.changeUserName("cc");};constchangeAge=()=>{
userStore.changeUserAge(20);};</script>
App.vue
- 这是一个简单的父组件,包含了
User和UpdataUser两个子组件。 - 在这个示例中,我们演示了如何在不同的组件中使用同一个 Pinia store。
<template><User /><UpdataUser/></template><script setup>import User from"./components/User.vue";import UpdataUser from'./components/UpdataUser.vue'</script>
实现效果:

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

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,// 持久化存储的位置},],},
现在就成功开启了持久化功能

可以看到我们修改数据后会在localStorage里添加数据,并且刷新页面数据也不会复原
总结
这篇文章详细介绍了如何在 Vue 组件中使用 Pinia 状态管理库。
使用 Pinia 与不使用 Pinia 时的组件通信方式相比,组件之间的通信更加简单和清晰
不再需要通过事件总线或 prop 逐层传递数据,而是可以直接访问共享的状态
这种模式可以让你的应用程序更加模块化和可维护
版权归原作者 小陈要努力呦 所有, 如有侵权,请联系我们删除。