Vue3 状态管理新选择:Pinia 从入门到实战

一、什么是pinia?

在 Vue3 生态中,状态管理一直是开发者关注的核心话题。随着 Vuex 的逐步淡出,Pinia 作为官方推荐的状态管理库,凭借其简洁的 API、强大的功能和对 Vue3 特性的完美适配,成为了新时代的不二之选。今天我们就来深入探讨 Pinia 的使用方法和最佳实践。

如果用生活中的例子来解释:

Pinia 就像一个 “共享冰箱”

想象一下,你和室友合租一套公寓,你们有一个 共享冰箱(Pinia Store)。这个冰箱的作用是:

  • 存放公共物品(状态 State):比如牛奶、水果、饮料等。
  • 规定取用规则(Getters):比如 “只能在早餐时间喝牛奶”。
  • 处理特殊操作(Actions):比如 “牛奶喝完后要通知所有人”。

pinia官网:Pinia | The intuitive store for Vue.js

二、怎么创建一个Pinia

1. 创建项目的时候直接选择Pinia

2. 项目中没有Pinia时,手动下载

①安装Pinia

npm install pinia

②在src中创建stores

③创建ts文件作为Pinia容器

④在counter.ts中加入以下代码

import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', () => {})

1、defineStore 是 Pinia 状态管理库 中的一个核心函数

2、参数说明
        第一个参数 'counter':store 的唯一标识符(不可重复)
        第二个参数 () => {}:store 的配置函数,返回 store 的状态和方法

 ⑤在main.ts中挂载Pinia

import './assets/main.css'import { createApp } from 'vue'
import { createPinia } from 'pinia'    // 引入创建Pinia方法
import App from './App.vue'const app = createApp(App)   app.use(createPinia())   // 挂载到app页面app.mount('#app')

这样就成功手动实现了Pinia的手动安转和配置了。


三、定义第一个Pinia

1、第一个选项式Pinia示例

基础语法:

import { defineStore } from 'pinia'// 定义并导出 Store
export const defineStore实例化对象名 = defineStore('状态管理对象名称', {// 状态初始化,相当于datastate: () => ({属性名1:属性值1}),// 计算属性(类似 Vue 组件中的 computed)getters: {},// 方法(类似 Vue 组件中的 methond)actions: {函数}
})

 基础示例:

/stores/counter.ts

// 1、导入defineStore
import { defineStore } from 'pinia'
import {computed,ref} from "vue";export const useCounterStore = defineStore('counter', {state() {    // 相当于组件中的datareturn {title:"选项式计数管理器",count: 25   // 状态管理变量数据}},getters: {     // 相当于组件中的computeddoubleCount: (state) => state.count * 2,   // 2倍计算属性数据doubleCountPlusOne: (state) => state.count + 10  // 加10计算属性数据},actions: {       // 相当于组件中的methodsincrement() {this.count++   // 创建函数每次点击按钮,count加1},decrement(){this.count--    // 创建函数每次点击按钮,count减1}}
})

/src/App.vue

<script setup lang="ts">
// 1、导入
import {useCounterStore} from "@/stores/counter.ts"
// 2、实例化
const counterStore = useCounterStore()
</script><template>
// 3、使用
<div class="app"><h2>标题{{counterStore.title}}</h2><p>当前计数:{{counterStore.count}}</p><p>双倍计数:{{counterStore.doubleCount}}</p><p>计数+10:{{counterStore.doubleCountPlusOne}}</p><button @click="counterStore.increment()">点击+1</button><button @click="counterStore.decrement()">点击-1</button>
</div>
</template>

 运行结果:

2、第一个组合式Pinia实例

基础语法:


import { defineStore } from 'pinia'
import { ref, computed, reactive } from 'vue'export const useStoreNameStore = defineStore('storeId', () => {// 1. State - 使用 ref 或 reactiveconst count = ref(0)const state = reactive({ name: 'example' })// 2. Getters - 使用 computedconst doubleCount = computed(() => count.value * 2)// 3. Actions - 普通函数function increment() {count.value++}// 4. 返回需要暴露的属性和方法return {count,doubleCount,increment}
})

基础示例:

/stores/users.ts

import {defineStore} from 'pinia'
import {computed, reactive, ref} from "vue";
export const useUserStore = defineStore('user', ()=>{let isLogin = ref(false);let username = ref('未知');let email = ref('未知');let displayName = ref('未知');let roles = reactive(['管理员','用户','玩家','游客']);let nowRole = ref('未知');let theme = ref('白色');let language = ref('chinese');let message = ref(0);function updateLoginStatus(){if(isLogin.value){isLogin.value = !isLogin.value;username.value = "未知";displayName.value = "未知";nowRole.value = "未知";message.value = 0;email.value = "未知";}else{isLogin.value = !isLogin.value;username.value = "张三";displayName.value = "追风少年";let random:number = Math.floor(Math.random()*4);nowRole.value = roles[random];message.value = 10;email.value = "zhangsan@163.com";}}function updateUserProfile(){theme.value = theme.value === 'white' ? 'dark' : 'white';language.value = language.value === 'chinese' ? 'english' : 'chinese';}function resetUser(){username.value = "未知";displayName.value = "未知";nowRole.value = "未知";message.value = 0;roles.splice(0,roles.length);email.value = "未知";}return {isLogin,username,email,displayName,nowRole,theme,language,message,updateLoginStatus,updateUserProfile,resetUser}
})

 /src/App.vue

<script setup lang="ts">
import { useUserStore } from "@/stores/users.ts";
const userStore = useUserStore();
</script>
<template>
<div class="user-profile"><h1>用户资料</h1><!-- 直接访问 --><p>用户名: {{ userStore.username }}</p><p>网名: {{ userStore.displayName }}</p><!-- 解构访问 --><div><p>邮箱: {{userStore.email }}</p><p>登录状态: {{ userStore.isLogin ? '登录':'未登录'}}</p></div><!-- 复杂数据访问 --><div><p>主题: {{ userStore.theme }}</p><p>语言: {{ userStore.language }}</p></div><!-- 数组数据 --><div><p>角色: {{ userStore.nowRole }}</p><p>未读通知: {{ userStore.message}}</p></div><button @click="userStore.updateLoginStatus" >{{ userStore.isLogin ? '退出':'登录'}}</button><button @click="userStore.updateUserProfile">更新资料</button><button @click="userStore.resetUser">重置用户</button>
</div>
</template>

运行结果: 

 

五、综合案例

/stores/counter.ts

import {reactive} from 'vue'
import { defineStore } from 'pinia'export const useCounterStore = defineStore('counter', () => {let products = reactive([{id: 1, 			    // 商品IDname: "苹果16ProMax", // 商品名称price: 12999,       // 商品价格category: "phone", // 商品类别num:123,     // 商品库存数量rating: 4.8,       // 商品评分},{id: 2,name: "联想拯救者",price: 23999,category: "laptop",   // 笔记本num: 0,rating: 3.8,},{id: 3,name: "华硕天选6pro",price: 11499,category: "laptop",   // 笔记本num: 15,rating: 4.9,},{id: 3,name: "iQoo平板7",price: 3499,category: "tablet",  // 平板电脑num: 899,rating: 3.7,},{id: 4,name: "iPad Air",price: 8599,category: "tablet",  // 平板电脑num: 899,rating: 4.1,},{id: 5,name: "小米手环7",price: 999,category: "watch",  // 手表num:45,rating: 4.61,},{id: 6,name: "苹果手表6",price: 3888,category: "watch",  // 手表num:45,rating: 4.9,},{id: 6,name: "小米手机",price: 3999,category: "phone",num:425,nun: 442,rating: 4.7,},],)let avgrating = products.reduce((sum,crr) => sum+crr.rating,0) / products.lengthlet avgprice = products.reduce((sum,crr) => sum+crr.price,0) / products.lengthlet sumNum = products.reduce((sum,crr) => sum+crr.num,0)let stockNum = products.filter(item=>item.num <= 0).lengthlet categories = reactive(["phone", "laptop", "tablet", "watch"])return {products,categories,avgrating,avgprice,sumNum,stockNum}
})

/src/App.vue 

<script setup lang="ts">
import {useCounterStore} from './stores/counter.ts'
import {computed, ref} from 'vue'
const store = useCounterStore()
const products = store.products
const minPrice = ref(0)
const maxPrice = ref(0)
const filteredProducts = computed(() => {return products.filter(item => item.price >= minPrice.value && item.price <= maxPrice.value)
})
</script><template><div class="container"><h1>库存管理系统</h1><h2>总产品数据</h2><table><tr><th>商品名</th><th>商品价格</th><th>商品库存</th><th>商品评分</th></tr><tr v-for="(item) in products"><td>{{item.name}}</td><td>{{item.price}}</td><td>{{item.num}}</td><td>{{item.rating}}</td></tr></table><strong>产品数量:{{store.sumNum}}</strong><strong>缺货商品数量:{{store.stockNum}}</strong><strong>平均价格:{{store.avgprice}}</strong><strong>平均评分:{{store.avgrating}}</strong><h2>分类统计</h2><div class="classify" v-for="item in store.categories"><strong>{{item}}</strong><table><tr><th>商品名</th><th>商品价格</th><th>商品库存</th><th>商品评分</th></tr><template v-for="product in products" :key="product.id"><tr v-if="product.category === item"><td>{{ product.name }}</td><td>{{ product.price }}</td><td>{{ product.num }}</td><td>{{ product.rating }}</td></tr></template></table></div><h2>商品筛选</h2><div><label>价格区间:</label><input type="number" v-model="minPrice">---<input type="number" v-model="maxPrice"><table v-if="filteredProducts.length > 0"><tr><th>商品名</th><th>商品价格</th><th>商品库存</th><th>商品评分</th></tr><tr v-for="item in filteredProducts" :key="item.id"><td>{{ item.name }}</td><td>{{ item.price }}</td><td>{{ item.num }}</td><td>{{ item.rating }}</td></tr></table><p v-else>暂无符合条件的商品。</p></div></div>
</template><style scoped>
.container{width:1000px;padding: 20px;border: #6e7681 1px solid;box-shadow: 2px 2px 4px #bdc1c6;margin: 0 auto;
}
h1{text-align: center;
}
table{margin: 0 auto;border: #6e7681 1px solid;width:1000px;border-collapse: collapse; /* 合并边框 */margin-bottom: 30px;
}
strong{display: block;margin-top: 10px;
}
th,tr,td{border: #6e7681 1px solid;  /* 关键:为单元格添加边框 */padding: 10px;              /* 添加内边距使内容更美观 */text-align: center;
}
</style>

运行结果:

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

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

相关文章

Unity相机控制

相机的控制无非移动和旋转&#xff0c;每种操作各3个轴6个方向&#xff0c;一共12种方式。在某些需要快速验证的项目或Demo里常常需要丝滑的控制相机调试效果。相机控制虽然不是什么高深的技术&#xff0c;但是要写的好用还是很磨人的。 锁定Z轴的旋转 一个自由的相机可以绕 …

vue2 使用liveplayer加载视频

vue2 使用liveplayer加载视频 官网: https://www.liveqing.com/docs/manuals/LivePlayer.html支持WebRTC/MP4播放;支持m3u8/HLS播放;支持HTTP-FLV/WS-FLV/RTMP播放;支持直播和点播播放;支持播放器快照截图;支持点播多清晰度播放;支持全屏或比例显示;自动检测IE浏览器兼容播放;支…

JavaScript语法树简介:AST/CST/词法/语法分析/ESTree/生成工具

AST简介 在平时的开发中&#xff0c;经常会遇到对JavaScript代码进行检查或改动的工具&#xff0c;例如ESLint会检查代码中的语法错误&#xff1b;Prettier会修改代码的格式&#xff1b;打包工具会将不同文件中的代码打包在一起等等。这些工具都对JavaScript代码本身进行了解析…

Java函数式编程之【基本数据类型流】

一、基本数据类型与基本数据的包装类 在Java编程语言中&#xff0c;int、long和double等基本数据类型都各有它们的包装类型Integer、Long和Double。 基本数据类型是Java程序语言内置的数据类型&#xff0c;可直接使用。 而包装类型则归属于普通的Java类&#xff0c;是对基本数据…

.NET Core部署服务器

1、以.NET Core5.0为例&#xff0c;在官网下载 下载 .NET 5.0 (Linux、macOS 和 Windows) | .NET 根据自己需求选择x64还是x86&#xff0c;记住关键下载完成还需要下载 Hosting Bundel &#xff0c;否则不成功 2、部署https将ssl证书放在服务器上&#xff0c;双击导入&#…

YOLO---04YOLOv3

YOLOV3 论文地址&#xff1a;&#xff1a;【https://arxiv.org/pdf/1804.02767】 YOLOV3 论文中文翻译地址&#xff1a;&#xff1a;【YOLO3论文中文版_yolo v3论文 中文版-CSDN博客】 YOLOv3 在实时性和精确性在当时都是做的比较好的&#xff0c;并在工业界得到了广泛应用 …

Qt知识点3『自定义属性的样式表失败问题』

问题1&#xff1a;自定义类中的自定义属性&#xff0c;如何通过样式表来赋值除了QT自带的属性&#xff0c;我们自定义的类中如果有自定义的静态属性&#xff0c;也可以支持样式表&#xff0c;如下 &#xff1a; Q_PROPERTY(QColor myBorderColor READ getMyBorderColor WRITE s…

RDQS_c和RDQS_t的作用及区别

&#x1f501; LPDDR5 中的 RDQS_t 和 RDQS_c — 复用机制详解 &#x1f4cc; 基本角色 引脚名 读操作&#xff08;READ&#xff09;作用 写操作&#xff08;WRITE&#xff09;作用&#xff08;当启用Link ECC&#xff09; RDQS_t Read DQS True&#xff1a;与 RDQS_c…

测试分类:详解各类测试方式与方法

前言&#xff1a;为什么要将测试进行分类呢&#xff1f;软件测试是软件生命周期中的⼀个重要环节&#xff0c;具有较高的复杂性&#xff0c;对于软件测试&#xff0c;可以从不同的角度加以分类&#xff0c;使开发者在软件开发过程中的不同层次、不同阶段对测试工作进行更好的执…

新手docker安装踩坑记录

最近在学习docker&#xff0c;安装和使用折腾了好久&#xff0c;在这里记录一下。下载# 依赖安装 sudo apt update sudo apt install -y \ca-certificates \curl \gnupg \lsb-release# 使用清华镜像源&#xff08;Ubuntu 24.04 noble&#xff09; echo \"deb [arch$(dpkg …

TOGAF指南1

1.TOGAF标准简介 TOGAF&#xff08;The Open Group Architecture Framework&#xff09;就像是一个企业架构的“操作手册”。它帮助企业设计、搭建和维护自己的“系统地图”&#xff0c;确保不同部门、技术、业务目标能像齿轮一样协调运转。 它的核心是&#xff1a; 用迭代的方…

[Linux入门] Linux 防火墙技术入门:从 iptables 到 nftables

目录 一、防火墙基础&#xff1a;netfilter 与 iptables 的关系 1️⃣什么是 netfilter&#xff1f; 2️⃣什么是 iptables&#xff1f; 二、iptables 核心&#xff1a;五链四表与规则体系 1️⃣什么是 “链”&#xff08;Chain&#xff09;&#xff1f; 2️⃣ 什么是 “…

函数fdopendir的用法

以下是关于 fdopendir 函数的详细解析&#xff0c;结合其核心功能、参数说明及典型应用场景&#xff1a;&#x1f50d; ‌一、函数功能与原型‌‌核心作用‌将已打开的目录文件描述符&#xff08;fd&#xff09;转换为目录流指针&#xff08;DIR*&#xff09;&#xff0c;用于后…

[源力觉醒 创作者计划]_文心4.5开源测评:国产大模型的技术突破与多维度能力解析

声明&#xff1a;文章为本人真实测评博客&#xff0c;非广告&#xff0c;并没有推广该平台 &#xff0c;为用户体验文章 一起来轻松玩转文心大模型吧&#x1f449; 文心大模型免费下载地址 一、引言&#xff1a;文心4.5开源——开启多模态大模型新时代 2025年6月30日&#x…

微信小程序无法构建npm,可能是如下几个原因

安装位置的问题&#xff0c;【npm安装在cd指定位置】小程序缓存的问题退出小程序&#xff0c;重新构建即可

从 MyBatis 到 MyBatis - Plus:@Options 注解的那些事儿

在 MyBatis 以及 MyBatis - Plus 的开发过程中&#xff0c;注解的使用是提升开发效率和实现特定功能的关键。今天我们就来聊聊 Options 注解&#xff0c;以及在 MyBatis - Plus 中它的使用场景和替代方案。 一、MyBatis 中的 Options 注解 在 MyBatis 框架中&#xff0c;Option…

转换图(State Transition Diagram)和时序图(Sequence Diagram)画图流程图工具

针对程序员绘制状态转换图&#xff08;State Transition Diagram&#xff09;和时序图&#xff08;Sequence Diagram&#xff09;的需求&#xff0c;以下是一些好用的工具推荐&#xff0c;涵盖在线工具、桌面软件和基于文本的工具&#xff0c;适合不同场景和偏好。这些工具在易…

基于php的在线酒店管理系统(源代码+文档+PPT+调试+讲解)

课题摘要在旅游住宿行业数字化转型的背景下&#xff0c;传统酒店管理存在房态更新滞后、预订渠道分散等问题。基于 PHP 的在线酒店管理系统&#xff0c;凭借其开发高效、兼容性强的特点&#xff0c;构建集客房管理、预订处理、客户服务于一体的综合性管理平台。 系统核心功能包…

视频质量检测中卡顿识别准确率↑32%:陌讯多模态评估框架实战解析

原创声明本文为原创技术解析&#xff0c;核心技术参数与架构设计引用自《陌讯技术白皮书》&#xff0c;禁止未经授权的转载与改编。一、行业痛点&#xff1a;视频质量检测的现实挑战在实时流媒体、在线教育、安防监控等领域&#xff0c;视频质量直接影响用户体验与业务可信度。…

流式输出阻塞原因及解决办法

流式输出不懂可看这篇文章&#xff1a;流式输出&#xff1a;概念、技巧与常见问题 正常情况&#xff0c;如下代码所示&#xff1a; async def event_generator():# 先输出数字1yield "data: 1\n\n"# 然后每隔2秒输出数字2&#xff0c;共输出10次for i in range(10):…