Pinia 实战指南:轻松驾驭前端状态管理
舜意 2024-08-25 13:03:03 阅读 84
前言
本文讲解一下在前端开发中经常使用的一个状态管理工具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组件接受一个名为 <code>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"code> 将
title
变量传递给 Child
组件的 title
prop。
<template>
<Child :title="title"></Child>code>
</template>
<script setup>
import { -- --> ref } from "vue";
import Child from "./components/Child.vue";
const title = "hello world";
</script>
实现效果:
子传父
Child.vue
Child.vue组件包含一个按钮,当点击按钮时会触发 <code>submit 方法。在 submit
方法中,使用 defineEmits
函数定义了一个名为 'onSub'
的自定义事件。通过 emit('onSub', success)
触发这个自定义事件,并传递一个名为 'success'
的数据。
<template>
<button @click="submit">提交</button>code>
</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"code> 监听
Child
组件触发的 'onSub'
事件。当 'onSub'
事件被触发时,会执行 handle
函数,并将 Child
组件传递的数据 'success'
赋值给 res
变量。最后在模板中显示 res
的值
<template>
<Child @onSub="handle"></Child>code>
<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>
实现效果:
使用公共变量通信
接下来我们去使用公共变量去通信,其实这个就有一点像pinia的通信方式了,但是这不是pinia,所以还不是很优雅,不过同样能够去实现通信的效果
global.js
<code>global.js 文件中定义了一个全局的 sum
变量,并通过 ref
函数将其包裹成一个响应式的引用
import { -- --> ref } from "vue";
export const 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>code>
</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
<code>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
<code>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
对象,包含 name
和 age
属性。
Actions:
actions
对象定义了三个方法:
changeUserInfo
: 用于更新整个 userInfo
对象。changeUserName
: 用于更新 userInfo.name
属性。changeUserAge
: 用于更新 userInfo.age
属性。
这些 action 方法可以在组件中调用,以改变 store 中的状态。
Getters:
getters
对象定义了一个计算属性 afterAge
,它会返回 userInfo.age
加 10 的结果。这个计算属性可以在组件中使用,就像使用组件的 computed 属性一样。
接下来我们就去使用它了
首先创建User.vue
这个组件直接使用 <code>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
这个组件也使用了 <code>useUserStore 获取 user store 实例。在 changeName
和 changeAge
方法中,我们调用了 store 的 changeUserName
和 changeUserAge
action 方法来更新状态。这里需要注意,不要直接修改 userStore.userInfo
对象,而是使用定义好的 action 方法。这样可以确保状态的更新是受控的,并且能够触发相关的副作用和订阅。
<template>
<div>
<button @click="changeName">修改名字</button>code>
<button @click="changeAge">修改名字</button>code>
</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
这是一个简单的父组件,包含了 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 持久化插件:
<code>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 逐层传递数据,而是可以直接访问共享的状态
这种模式可以让你的应用程序更加模块化和可维护
上一篇: FUXA:基于Web的工艺可视化(SCADA/HMI/仪表板)软件安装与使用指南
下一篇: 【JavaWeb项目】——外卖订餐系统之商家添加餐品、修改餐品、查询热卖餐品、查询出售车、进行发货操作
本文标签
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。