重塑状态管理的艺术:Vue3中Pinia的魔法之旅内包含简易购物车案例

前言

Vue.js的世界里,每一次更新都是一次进化,Vue3携带着更强大的性能与灵活性翩然而至。而在这场技术盛宴中,Pinia以一种优雅而革命性的方式,重新定义了状态管理的体验。如果说VuexVue2时代的王者,那么Pinia无疑是Vue3新时代的璀璨明星,它不仅简化了状态管理的复杂度,还极大地提升了开发者的幸福感。接下来,让我们携手踏上这场探索Pinia魅力的魔法之旅。
如果想了解更全面的vue3,Vue3+TS+Vite+Pinia最全总结>>>

Pinia:Vue3的灵魂伴侣

Vue3引入了Composition API,为组件的状态和逻辑提供了更清晰、更模块化的组织方式。Pinia,作为Vue3的官方状态管理库,完美地融入了这一新体系,它不仅仅是Vuex的简单升级,而是在设计理念上的一次革新。

  • 直观简洁:告别繁复的actionsmutationsPinia提倡直接操作状态,让你的代码更加直观易懂。
  • TypeScript友好:原生支持TypeScript,为你的状态管理提供强类型检查,减少错误,提升开发效率。
  • 插件生态:尽管年轻,Pinia已经拥有丰富的插件生态,无论是持久化状态、调试,还是更复杂的业务需求,都能找到合适的解决方案。

pinia为什么取代了vuex官方的说法:

  1. 取消 mutations。因为在大部分开发者眼中,mutations 只支持 同步修改状态数据,而 actions 虽然支持 异步,却依然要在内部调用 mutations 去修改状态,无疑是非常繁琐和多余的。
  2. 所有的代码都是 TypeScript 编写的,并且所有接口都尽可能的利用了 TypeScript 的 类型推断,而不像 Vuex 一样需要自定义 TS 的包装器来实现对 TypeScript 的支持
  3. 不像 Vuex 一样需要在实例Vue原型上注入状态依赖,而是通过直接引入状态模块、调用 getter/actions 函数来完成状态的更新获取;并且因为自身对 TypeScript 的良好支持和类型推断,开发者可以享受很优秀的代码提示
  4. 不需要预先注册状态数据,默认情况下都是根据代码逻辑自动处理的;并且可以在使用中随时注册新的状态
  5. 没有 Vuexmodules 嵌套结构,所有状态都是扁平化管理的。也可以理解为 pinia 注册的状态都类似 vuexmodule,只是 pinia 不需要统一的入口来注册所有状态模块
  6. 虽然是扁平化的结构,但是依然支持 每个状态之间的互相引用和嵌套
  7. 不需要 namespace 命名空间,得利于扁平化结构,每个状态在注册时即使没有声明状态模块名称,pinia 也会默认对它进行处理
    总结一下就是:Pinia 在实现 Vuex 全局状态共享的功能情况下,改善了状态存储结构,优化了使用的方式,简化 API 设计与规范,并且基于 TypeScript 的类型推断,为开发者提供了良好的 TypeScript支持与代码提示。

pinia学习实践

一、安装Pinia

cd进入项目根目录,执行安装pinia

npm install pinia@2.1.4

二、全局中蹙额pinia

创建pinia实例并传递给应用,注册 pinia 实例到全局,在main.ts中创建pinia实例并传递给应用。

import './assets/main.css'
import { createApp } from 'vue'
import App from './App.vue'`在这里插入代码片`
// 导入pinia实例方法
import { createPinia } from 'pinia'
// 创建pinia实例并挂载到应用上
const app = createApp(App).use(createPinia())
app.mount('#app')

三、 创建一个 Store,定义和修改Store状state

  • Store(如:Pinia)是一个保存状态和业务逻辑的实体,不与组件树绑定。它承载着全局状态(整个应用中访问的数据),每个引入它的组件都可以对其进行读写操作。
  • Store 中有三个类似组件的概念:state(data)getter(computed)action(methods)
  • 需要注意的是,并不是所有的数据都要放到 Store 中处理,像购物车、用户登录状态等这些需要快速处理跨组件之间的状态,使用 Store 更加便捷;而像控制元素是否可见这种组件内状态,则不需要Store 了。

通过 defineStore() 方法定义 Store(全局状态数据),其中参数和返回值说明:

  • 参数1:应用中 Store 的唯一标识字符串Id,不可重复。
  • 参数2:接受Option对象或 Setup 函数形式。
  • 返回值: defineStore() 的返回值进行任意命名,但最好使用 store 的名字,同时以 use 开头且以 Store 结 尾。
  • use开头是一个符合组合式函数风格的约定。(比如 useUserStore , useCartStore ,useProductStore )。

四、创建一个简单的购物车数据状态管理文件

/src/stores/cart.ts

import { defineStore } from "pinia";
interface Goods{id:number;name:string;num:number;
}
// 参数二,采用option对象形式
export const useCartStore = defineStore('cart',{// state用于定义一个返回初始状态的函数state:()=>{return {name:"wuyong",age:18,goodsList:[{id:1,name:'手机',num:1}] as Goods[]}}
})

五、结构state状态组件中使用

组件中获取和修改状态数据,解构出来的state状态,不是响应式的,使用 storeToRefs 方法将状态转成ref后,解构出来的状态才是响应式的,通过store实例操作状态是 响应式 的,通过 $patch 可同时修改多个状态属性。
如下表示:

<script setup lang="ts">
import { useCartStore } from './stores/cart';
import { storeToRefs } from 'pinia';
const cartStore = useCartStore();
// 通过store实例获取state状态
console.log("获取state状态", cartStore.$state, cartStore.name);
// 直接结构出来state状态,不是响应式的
//let { age } = cartStore;
// 使用storeToRefs方法将状态转成ref后,结构出来的状态是响应式的
const { age } = storeToRefs(cartStore);
console.log("age", age.value)
//修改state状态值
function handleAddAge() {// age ++; // 直接解构是`非响应式`的// age.value++; // 转成ref是`响应式`的// cartStore.age++;// 4. 通过 $patch 方法接受一个对象,可同时修改多个状态属性// cartStore.$patch({//   name: 'wuqi',//   age: cartStore.age+1,// });// $patch 方法接受一个带state参数的回调函数,state来操作状态cartStore.$patch((state) => {state.name = 'wuqi';state.age = 24;state.goodsList.push({ id: state.goodsList.length + 1, name: '电脑'+state.goodsList.length + 1, num: 2 });})
}
</script>
<template><div><p>用户名:{{ cartStore.name }}</p><p>年龄:{{ age }}<button @click="handleAddAge">+1</button></p><ul><li v-for="(item, index) in cartStore.goodsList" :key="index">商品ID: {{ item.id }},名称:{{ item.name }},数量:{{ item.num }}</li></ul><button @click="handleAddAge">年龄+1</button></div>
</template>
<style scoped></style>

效果:
重塑状态管理的艺术:Vue3中Pinia的魔法之旅

六、重置状态:通过调用 store$reset() 方法,将 state 状态重置为初始值

<script setup lang="ts">
import { useCartStore } from '@/stores/counter';
import { storeToRefs } from 'pinia';
const cartStore = useCartStore();
function handleResetState() {// 将 state 状态重置为初始值cartStore.$reset();
}
</script>
<template>
<div><button @click="handleResetState">重置状态</button>
</div>
</template>
<style scoped>
</style>

效果:
重塑状态管理的艺术:Vue3中Pinia的魔法之旅

Getters派生属性

Getter 介绍

  • 有时候我们需要从 Store 中的 state 中派生出一些状态。例如:基于上面代码,增加一个userType 属性,当 age 值小于18,则 userType 值为 未成年人 ; 大于等 于18 , 则 userType 值为 成年人 。这时我们就需要用到 Getter 为我们解决。
  • Getter 相当于组件中的计算属性(computed)。可以通过 defineStore() 中的 getters 属性来定义它们。 推荐使用箭头函数,并且它将接收 state 作为第一个参数。
  • Getter中通过返回一个函数,该函数可以接受调用方传递任意参数。

总结:Getter 完全等同于 storestate 的计算值。可以通过 defineStore() 中的 getters 属性来定义它们。推荐使用箭头函数,并且它将接收 state 作为第一个参数

Getter 实操作

一、定义状态(state)、派生属性(getters),创建一个根据商品ID查找商品的getGoodsById,然后通过查找函数find从商品列表中匹配指定ID的商品并返回。

import { defineStore } from "pinia";
interface Goods{id:number;name:string;num:number;
}
// 参数二,采用option对象形式
export const useCartStore = defineStore('cart',{// state用于定义一个返回初始状态的函数state:()=>{return {name:"wuyong",age:18,goodsList:[{id:1,name:'手机',num:1}] as Goods[]}},//定义派生属性(等同于计算属性,根据state状态值得到新的一个状态值)getters:{//接受state作为第一个参数,使用建通函数声明:箭头函数方法不能使用thisuserType:(state)=>{return state.age<18?'未成年人':'成年人'},//使用普通函数声明,方法体可以使用this访问整个store实例getGoodsById(state){//通过this获取上面getterconsole.log("getter普通函数",this.userType);return (id:number)=>this.goodsList.find(item=>item.id===id)}}
})

二、在组件中获取Getter

<p>Getter 派生属性:{{ cartStore.userType }}</p>
<p>向 Getter 派生属性传递参数:{{ cartStore.getGoodsById(1) }}</p>

重塑状态管理的艺术:Vue3中Pinia的魔法之旅

actions属性

一、actions介绍
        actions属性扮演着关键角色,它们用于封装业务逻辑,执行异步操作,并且能够修改Store中的状态。与直接在state中修改不同,actions提供了更好的组织结构和可维护性,同时也便于跟踪状态变更的来源。下面是对actions属性的详细说明:

  • Action 是一个在Pinia Store中定义的方法,用于处理包含副作用或异步逻辑的操作。
  • 它们是Side Effect(副作用)的安全场所,比如调用API、修改状态(通过this.$patch或直接修改状态对象)、延时操作等。
  • Action 函数可以返回Promise,因此支持异步操作,这在处理网络请求时特别有用。
  • Vue的组件类似,Actions内部可以使用Composition API的函数,如ref、computed等。

二、如何定义Actions

export const useCartStore = defineStore('cart', {// ... state 和 gettersactions: {// 添加商品到购物车的Actionasync addToCart(product: Goods) {// 模拟异步操作,如调用后端APIawait new Promise(resolve => setTimeout(resolve, 1000));// 使用this访问状态并修改this.goodsList.push({ ...product, num: 1 });},// 清空购物车的ActionclearCart() {this.goodsList = []; // 直接修改状态数组}}
});

三、使用Actions
在组件内,通过从Store中解构出Action方法,可以直接在组件的方法或生命周期钩子中调用它们。

<script setup>
import { useCartStore } from './cart.store';const cartStore = useCartStore();async function onAddToCart(product) {await cartStore.addToCart(product);
}function clearCart() {cartStore.clearCart();
}
</script>
Action购物车使用实例

将商品加入购物车 goodsList 状态中。

import { defineStore } from "pinia";
interface Goods{id:number;name:string;num:number;
}
// 参数二,采用option对象形式
export const useCartStore = defineStore('cart',{// state用于定义一个返回初始状态的函数state:()=>{return {name:"wuyong",age:15,goodsList:[{id:1,name:'手机',num:1}] as Goods[]}},//定义派生属性(等同于计算属性,根据state状态值得到新的一个状态值)getters:{//接受state作为第一个参数,使用建通函数声明:箭头函数方法不能使用thisuserType:(state)=>{return state.age<18?'未成年人':'成年人'},//使用普通函数声明,方法体可以使用this访问整个store实例getGoodsById(state){//通过this获取上面getterconsole.log("getter普通函数",this.userType);return (id:number)=>this.goodsList.find(item=>item.id===id)}},// 定义行为,类似methodactions:{//新增商品addGoods(goods:Goods){if(goods.num){//将goods.nums的值除1转成数值goods.num = goods.num/1//查询购物车中是否存在此商品,存在则数量累加,不存在则追加商品到数组中const target = this.goodsList.find(item=>item.id === goods.id)if(target){target.num+=goods.num}else{this.goodsList.push(goods)}}}}
})

组件中使用

<script setup lang="ts">
import { useCartStore } from './stores/cart';
import { storeToRefs } from 'pinia';
import { ref } from 'vue';
//加入购物车逻辑
const goodsId = ref<number>(1)
const goodsName = ref<string>('')
const goodsNum = ref<number>(1)
function handleAddCart() {const goods = { id: goodsId.value, name: goodsName.value, num: goodsNum.value }//触发store中的actioncartStore.addGoods(goods)
}
const cartStore = useCartStore();
// 通过store实例获取state状态
console.log("获取state状态", cartStore.$state, cartStore.name);
// 使用storeToRefs方法将状态转成ref后,结构出来的状态是响应式的
const { age } = storeToRefs(cartStore);
console.log("age", age.value)
//将state状态重置为初始值
function handleResetState() {cartStore.$reset();
}
//修改state状态值
function handleAddAge() {// $patch 方法接受一个带state参数的回调函数,state来操作状态cartStore.$patch((state) => {state.name = 'wuqi';state.age = ++state.age;state.goodsList.push({ id: state.goodsList.length + 1, name: '电脑' + state.goodsList.length + 1, num: 2 });})cartStore.$subscribe((mutation, state) => {console.log("mutation, state", mutation, state)// 获取组件标识id(和 cartStore.$id 一样)const { storeId } = mutation;console.log('storeId', storeId); // 'cart'// 每当状态发生变化时,将整个 state 持久化到本地存储。localStorage.setItem('cart', JSON.stringify(state))})
}
</script><template><div><p>用户名:{{ cartStore.name }}</p><p>年龄:{{ age }}</p><ul><li v-for="(item, index) in cartStore.goodsList" :key="index">商品ID: {{ item.id }},名称:{{ item.name }},数量:{{ item.num }}</li></ul><input v-model="goodsId" placeholder="商品ID"> &nbsp;<input v-model="goodsName" placeholder="商品名称"> &nbsp;<input v-model="goodsNum" placeholder="商品数量"> &nbsp;<button @click="handleAddCart">加入购物车</button><!-- <p>Getter 派生属性:{{ cartStore.userType }}</p><p>向 Getter 派生属性传递参数:{{ cartStore.getGoodsById(1) }}</p> --></div>
</template><style scoped></style>

最终效果:
重塑状态管理的艺术:Vue3中Pinia的魔法之旅内包含简易购物车案例

注意事项
Action内部应当尽量避免直接修改状态,推荐使用this.$patch或返回一个新的状态对象来更新状态,这样可以更好地利用Vue的响应式系统。
对于异步操作,务必确保正确处理异常,比如使用try-catch块来捕获错误并进行适当处理。

PiniaSetup函数形

一、PiniaSetup函数形式提供了一种更加灵活和现代的方式来组织和管理应用的状态,尤其适合那些喜欢Vue 3新特性的开发者。

二、使用场景

  • 当你偏好使用Vue 3的Composition API风格编码时。
  • 当你需要在Store内部使用其他Composition API功能,如watch、onMounted等。
  • 当你希望利用Vue的响应式系统来更精细地控制状态变化时。

三、示例代码解释

import {defineStore} from 'pinia'
import {ref,computed} from 'vue'
/**
* defineStore 参数2:采用 Setup 函数形式
* ref() 就是 state 属性
* computed() 就是 getters
* function() 就是 actions
*/
export const useCounterStore = defineStore('counter',()=>{// 定义store的state属性const count = ref(0)// 使用 computed 来定义派生状态(getters)const doubleCount = computed(()=>{count.value})function addCount(){count.value++}return {count,doubleCount,addCount}
})

组件中调用使用

<script setup lang="ts">
import {useCounterStore} from './stores/counter'
const counterStore = useCounterStore()
const { doubleCount} = cartStore;
</script><template><h2>Setup Store</h2><p>count:{{ doubleCount }}</p><button @click="counterStore.addCount">+1</button>
</template><style scoped></style>

重塑状态管理的艺术:Vue3中Pinia的魔法之旅内包含简易购物车案例
完结,更多vue3学习总结>>>>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/24087.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【NLP】2、大语言模型综述

一、背景和发展历程 大语言模型四个训练阶段&#xff1a; 预训练&#xff1a; 利用海量的训练数据&#xff0c;包括互联网网页、维基百科、书籍、GitHub、 论文、问答网站等&#xff0c;构建包含数千亿甚至数万亿单词的具有多样性的内容。利用由数千块高性能 GPU 和高速网络组成…

使用2个手机文件恢复工具,轻松找回文件

在这个智能手机横行的时代&#xff0c;手机已经成为我们生活中不可或缺的一部分。然而&#xff0c;就像生活中的一切事物一样&#xff0c;手机也有可能出现意外&#xff0c;比如文件丢失。这就像是你在超市购物&#xff0c;结果发现钱包不见了&#xff0c;那种感觉真是让人抓狂…

STM32项目分享:智能家居语音系统

目录 一、前言 二、项目简介 1.功能详解 2.主要器件 三、原理图设计 四、PCB硬件设计 1.PCB图 2.PCB打板焊接图: 五、程序设计 六、实验效果 七、包含内容 项目分享 一、前言 项目成品图片&#xff1a; 哔哩哔哩视频链接&#xff1a; https://www.bilibili.com…

Nvidia/算能 +FPGA+AI大算力边缘计算盒子:自动清理机器

总部位于硅谷的 ViaBot 正在为用于企业的机器人进行试行测试。 2016 年&#xff0c;Gregg Ratanaphanyarat 和 Dawei Ding从宾州州立大学辍学后&#xff0c;创办了一家户外清洁机器人初创公司。 如今&#xff0c;这场赌博似乎正在取得回报。二人的初创公司 ViaBot 正在与一家…

【YOLOV8】4.图片分类-训练自己的数据集

Yolo8出来一段时间了,包含了目标检测、实例分割、人体姿态预测、旋转目标检测、图像分类等功能,所以想花点时间总结记录一下这几个功能的使用方法和自定义数据集需要注意的一些问题,本篇是第四篇,图像分类功能,自定义数据集的训练。 YOLO(You Only Look Once)是一种流行的…

多目标应用:NSGA2求解无人机三维路径规划(MATLAB代码)

详细介绍 多目标应用&#xff1a;基于非支配排序的鱼鹰优化算法NSOOA求解无人机三维路径规划&#xff08;MATLAB代码&#xff09;-CSDN博客 一次运行结果 完整MATLAB代码 多目标应用&#xff1a;NSGA2求解无人机三维路径规划&#xff08;MATLAB代码&#xff09;

计算机毕业设计python+hadoop+spark猫眼电影票房预测 电影推荐系统 猫眼电影爬虫 电影数据可视化 电影用户画像系统 协同过滤算法 数据仓库

山东青年政治学院毕业论文&#xff08;设计&#xff09;开题报告 学生姓名 高宜凡 学 号 202010520237 所在学院 信息工程学院 专 业 信息管理与信息系统&#xff08;云计算与大数据技术&#xff09; 指导教师姓名 李海斌 黄虹 指导教师职称 工程师 副教授 指导教…

如何将照片从Android传输到笔记本电脑?

目前全球大部分照片都是由手机拍摄的。唯一的问题是这些照片会占用您的内部存储或 SD 卡上的大量空间。如果您的Android设备存储空间不足&#xff0c;您可能会被迫将照片从Android手机传输到笔记本电脑。您访问此网站只是因为您想了解如何将图片从Android传输到笔记本电脑。 如…

frp开启Dashboard

公网服务器 公网服务器frps.toml bindPort 81 # 127.0.0.1 Dashboard 端口&#xff0c;后面会进行内网穿透&#xff0c;因此不用配置ip为0.0.0.0 webServer.port 82auth.token "token令牌"公网服务器frpc.toml # 因为是在公网服务器的frpc所以直接配127.0.0.1 …

域内攻击 -----> Kerberoasting

在域中&#xff0c;能拿到账户信息得攻击基本上有四个 域内用户枚举域内密码喷洒KerberoastingAS-REP Roasting 对于AS-REP Roasting&#xff0c;我们下一篇文章讲&#xff0c;而前两个&#xff0c;请参考我以前域内横向移动得文章。 那么我们今天就来聊聊Kerberoasting 1.S…

C++基础一:代码编译和运行时的调用堆栈

目录 运行时进程的虚拟地址划分 函数调用堆栈 程序编译链接原理 运行时进程的虚拟地址划分 每一个进程的用户空间是私有的&#xff0c;内核空间是共享的 函数调用堆栈 程序编译链接原理

Three.js零基础入门教程

参考资料&#xff1a;Threejs中文网 1. threejs文件包下载和目录简介 在正式学习Three.js之前&#xff0c;先做一些必要的准备工作&#xff0c;具体说就是下载threejs官方文件包&#xff0c;threejs官方文件包提供了很多有用的学习资源。 Three.js版本问题 Three.js处于飞速…

《数学学习与研究》投稿难度大吗?

《数学学习与研究》杂志的投稿难度相对适中。 一方面&#xff0c;它作为一本有一定影响力的数学专业期刊&#xff0c;对稿件的质量有一定要求。论文需要具备一定的创新性、科学性和逻辑性&#xff0c;研究内容要具有一定的价值和深度。 另一方面&#xff0c;与一些核心期刊相…

TEE的存储系统是如何实现的?如何保证其安全的?

在一般情况下&#xff0c;TEE OS 中是没有磁盘的&#xff0c;也没有 EMMC/UFS 驱动&#xff0c;因此无法在 TEE OS 侧完成数据落盘。 那么&#xff0c;TEE OS 的安全存储是如何实现的呢&#xff1f;事实上&#xff0c;TEE OS 侧的安全存储是通过反向存储在 REE 侧来实现的。有…

web安全基础学习笔记

这里写目录标题 1.使用hackbar2.php漏洞基本分析 弱类型语言2.2 php漏洞找到隐藏的源代码之 index.php~2.3 php漏洞找到隐藏的源代码之 vim的临时文件 /.index.php.swp3.php漏洞基本分析 数组 3.php漏洞基本分析 extract4.php漏洞基本分析 strpos eregi函数漏洞4.php漏洞基本分…

携手星展银行,YonSuite财资管理助力企业“扬帆出海”

在全球经济一体化的浪潮下&#xff0c;越来越多的中国企业怀揣着“扬帆出海”的雄心壮志&#xff0c;积极开拓国际市场。然而&#xff0c;在海外市场面临的复杂环境和激烈竞争中&#xff0c;如何高效管理财务资金、优化资源配置、降低运营风险&#xff0c;成为摆在众多出海企业…

Qt图像处理技术十二:QImage实现边缘检测(sobel算法)

效果图 原理 Sobel算法是一种常用的边缘检测算法&#xff0c;它利用图像的灰度变化来检测图像中物体的边缘。Sobel算法主要包括以下几个步骤&#xff1a; 灰度化&#xff1a; 首先将彩色图像转换为灰度图像&#xff0c;因为灰度图像只包含单通道的灰度信息&#xff0c;有利于…

通过fiftyone按分类下载open-images-v7数据集,并转成yolov5可直接训练的格式

import osimport fiftyone as fo import fiftyone.zoo as foz import yamlclasses [Person, # 人 - 0Car, # 轿车 - 1Taxi, # 出租车 - 2Ambulance, # 救护车 - 3Bus, # 公共汽车 - 4Bicycle, # 自行车 - 5Motorcycle, # 摩托车 - 6Dog, # 狗 - 7Cat, # 猫 - 8M…

000002 - Hadoop环境安装

Hadoop及其大数据生态圈 1. 背景2. 实践2.1 Linux服务器准备2.2 在其中一台服务器上安装JDK2.3 在其中一台服务器上安装HADOOP2.4 本地模式运行一个hadoop案例 3. 自动化部署 1. 背景 要搭建Hadoop集群环境&#xff0c;我们需要执行如下 准备三台Linux服务器&#xff0c;服务…