六十天前端强化训练之第二十七天之Pinia 状态管理全解与购物车实战案例
=====欢迎来到编程星辰海的博客讲解======
看完可以给一个免费的三连吗,谢谢大佬!
目录
一、Pinia 深度解析
1. Pinia 核心设计
2. 核心概念图解
3. Store 类型对比
Option Store(选项式)
Setup Store(组合式)
4. 响应式原理
二、购物车状态管理实战
1. 项目结构
2. 核心 Store 实现(stores/cart.js)
3. 商品列表组件(ProductList.vue)
4. 购物车组件(ShoppingCart.vue)
三、实现效果说明
四、学习要点总结
五、扩展阅读推荐
官方资源
优质文章
进阶教程
一、Pinia 深度解析
1. Pinia 核心设计
Pinia 是 Vue 官方推荐的新一代状态管理库,具有以下核心优势:
- TypeScript 原生支持:完整的类型推断和自动补全
- 模块化架构:天然支持代码分割和按需加载
- 轻量无冗余:相比 Vuex 减少 40% 的代码量
- 组合式 API:完美对接 Vue3 的组合式编程范式
- Devtools 整合:完整的时间旅行调试支持
2. 核心概念图解
3. Store 类型对比
Option Store(选项式)
JAVASCRIPT
export const useCounterStore = defineStore('counter', { state: () => ({ count: 0 }), getters: { double: (state) => state.count * 2, }, actions: { increment() { this.count++ }, }, })
Setup Store(组合式)
JAVASCRIPT
export const useCounterStore = defineStore('counter', () => { const count = ref(0) const double = computed(() => count.value * 2) function increment() { count.value++ } return { count, double, increment } })
4. 响应式原理
Pinia 基于 Vue3 的 reactive() 实现响应式系统:
- State 自动转换为 reactive 对象
- Getters 使用 computed 实现计算缓存
- Actions 作为状态操作方法
二、购物车状态管理实战
1. 项目结构
TEXT
/src ├── stores │ └── cart.js ├── components │ ├── ProductList.vue │ └── ShoppingCart.vue └── App.vue
2. 核心 Store 实现(stores/cart.js)
JAVASCRIPT
import { defineStore } from 'pinia' import { computed, ref } from 'vue' export const useCartStore = defineStore('cart', () => { // 状态定义 const items = ref([]) // 购物车商品数组 const taxRate = 0.1 // 税率(常量) // Getter(带参数的派生状态) const cartTotal = computed(() => items.value.reduce((sum, item) => sum + item.price * item.quantity, 0) ) const totalWithTax = computed(() => cartTotal.value * (1 + taxRate) ) // Action:添加商品(幂等操作) const addToCart = (product, quantity = 1) => { const existing = items.value.find(item => item.id === product.id) if (existing) { // 商品存在时增加数量 existing.quantity += quantity } else { // 新商品添加购物车 items.value.push({ ...product, quantity, addedAt: new Date().toISOString() }) } } // Action:移除商品(支持完全移除和减少数量) const removeFromCart = (productId, quantity = 1) => { const index = items.value.findIndex(item => item.id === productId) if (index === -1) return if (items.value[index].quantity <= quantity) { // 完全移除商品 items.value.splice(index, 1) } else { // 减少商品数量 items.value[index].quantity -= quantity } } // Action:清空购物车 const clearCart = () => { items.value = [] } return { items, cartTotal, totalWithTax, addToCart, removeFromCart, clearCart } })
3. 商品列表组件(ProductList.vue)
VUE
<script setup> import { useCartStore } from '@/stores/cart' const cartStore = useCartStore() const products = [ { id: 1, name: 'Vue T-Shirt', price: 29.99 }, { id: 2, name: 'Pinia Mug', price: 15.50 }, { id: 3, name: 'Vuex Book', price: 39.99 } ] </script> <template> <div class="product-list"> <h2>Available Products</h2> <div v-for="product in products" :key="product.id" class="product-item"> <h3>{{ product.name }}</h3> <p>Price: \${{ product.price.toFixed(2) }}</p> <button @click="cartStore.addToCart(product)"> Add to Cart </button> </div> </div> </template>
4. 购物车组件(ShoppingCart.vue)
VUE
<script setup> import { useCartStore } from '@/stores/cart' import { storeToRefs } from 'pinia' const cartStore = useCartStore() const { items, cartTotal, totalWithTax } = storeToRefs(cartStore) </script> <template> <div class="shopping-cart"> <h2>Shopping Cart</h2> <div v-if="items.length === 0" class="empty-cart"> Your cart is empty </div> <div v-else> <div v-for="item in items" :key="item.id" class="cart-item"> <h3>{{ item.name }}</h3> <p> Quantity: {{ item.quantity }} <button @click="cartStore.removeFromCart(item.id, 1)">-</button> <button @click="cartStore.addToCart(item)">+</button> </p> <p>Price: \${{ (item.price * item.quantity).toFixed(2) }}</p> <button @click="cartStore.removeFromCart(item.id, item.quantity)"> Remove </button> </div> <div class="totals"> <p>Subtotal: \${{ cartTotal.toFixed(2) }}</p> <p>Tax (10%): \${{ (totalWithTax - cartTotal).toFixed(2) }}</p> <p class="total">Total: \${{ totalWithTax.toFixed(2) }}</p> </div> <button @click="cartStore.clearCart" class="clear-btn"> Clear Cart </button> </div> </div> </template>
三、实现效果说明
(图示说明:用户添加商品、调整数量、删除商品时,各组件自动同步状态)
- 状态共享:多组件同时访问同一购物车状态
- 响应式更新:价格计算实时同步
- 操作隔离:商品增减逻辑集中管理
- 类型安全:完整的TS类型推断
四、学习要点总结
-
核心概念
- Store 作为状态容器
- State 定义应用状态
- Getters 实现派生数据
- Actions 封装业务逻辑
-
最佳实践
JAVASCRIPT
// 正确的状态解构方式 const { count } = storeToRefs(store) // 错误的方式(破坏响应式) const { count } = store
-
架构原则
- 单一职责:每个Store聚焦特定领域
- 低耦合:组件不直接操作其他Store
- 高内聚:相关逻辑集中管理
-
调试技巧
JAVASCRIPT
// 浏览器控制台访问 const store = useCartStore() store.$patch({...}) store.$subscribe((mutation) => { console.log('状态变更:', mutation) })
五、扩展阅读推荐
官方资源
- Pinia 官方文档
- Vue 状态管理指南
- Pinia 与 Vuex 对比指南
优质文章
- 深入理解 Pinia 架构设计
- 企业级 Pinia 最佳实践
- Pinia 插件开发指南
- SSR 场景下的 Pinia 使用
- Pinia 单元测试全攻略
进阶教程
JAVASCRIPT
// 持久化插件示例 import { createPinia } from 'pinia' import { createPersistedState } from 'pinia-plugin-persistedstate' const pinia = createPinia() pinia.use(createPersistedState({ auto: true, // 自动持久化所有Store storage: sessionStorage }))
建议运行示例并通过 Vue Devtools 观察状态变更过程,加深理解。义和测试用例。