元素码农
基础
UML建模
数据结构
算法
设计模式
网络
TCP/IP协议
HTTPS安全机制
WebSocket实时通信
数据库
sqlite
postgresql
clickhouse
后端
rust
go
java
php
mysql
redis
mongodb
etcd
nats
zincsearch
前端
浏览器
javascript
typescript
vue3
react
游戏
unity
unreal
C++
C#
Lua
App
android
ios
flutter
react-native
安全
Web安全
测试
软件测试
自动化测试 - Playwright
人工智能
Python
langChain
langGraph
运维
linux
docker
工具
git
svn
🌞
🌙
目录
▶
Vue3 基础
▶
环境搭建
安装与配置
项目创建
开发工具
▶
模板语法
插值表达式
指令系统
事件处理
▶
核心概念
▶
响应式系统
ref与reactive
计算属性
侦听器
▶
组合式API
setup函数
生命周期钩子
自定义Hooks
▶
组件开发
▶
组件基础
组件通信
Props详解
插槽机制
▶
高级组件
动态组件
异步组件
递归组件
▶
状态管理
▶
Vuex基础
状态存储
模块化
组合式写法
▶
Pinia
创建Store
状态操作
插件机制
发布时间:
2025-03-22 12:28
↑
☰
# Pinia Store创建详解 本文将详细介绍Vue3中Pinia的Store创建方法,帮助你深入理解和掌握这个新一代的Vue状态管理方案。 ## 什么是Pinia? Pinia是Vue的官方状态管理库,它提供了一个更简单、更现代化的API,完全支持TypeScript,并且与Vue3的组合式API完美配合。相比Vuex,Pinia具有以下优势: - 更简洁的API - 更好的TypeScript支持 - 更轻量级 - 不需要嵌套模块 - 不需要命名空间 ## 安装和配置 ### 安装Pinia ```bash npm install pinia ``` ### 在Vue应用中注册 ```javascript // main.js import { createApp } from 'vue' import { createPinia } from 'pinia' import App from './App.vue' const pinia = createPinia() const app = createApp(App) app.use(pinia) app.mount('#app') ``` ## Store的定义 ### 基础Store ```typescript // stores/counter.ts import { defineStore } from 'pinia' export const useCounterStore = defineStore('counter', { // 状态 state: () => ({ count: 0, name: 'Counter' }), // Getter getters: { doubleCount: (state) => state.count * 2, // 带参数的getter doubleCountPlus: (state) => { return (amount: number) => state.count * 2 + amount } }, // Action actions: { increment() { this.count++ }, async fetchCount() { const response = await fetch('/api/count') this.count = await response.json() } } }) ``` ### 组合式API风格的Store ```typescript // stores/user.ts import { defineStore } from 'pinia' import { ref, computed } from 'vue' export const useUserStore = defineStore('user', () => { // 状态 const user = ref(null) const token = ref(null) // Getter const isLoggedIn = computed(() => !!token.value) const username = computed(() => user.value?.name ?? 'Guest') // Action async function login(credentials) { try { const response = await fetch('/api/login', { method: 'POST', body: JSON.stringify(credentials) }) const data = await response.json() user.value = data.user token.value = data.token } catch (error) { console.error('Login failed:', error) throw error } } function logout() { user.value = null token.value = null } return { user, token, isLoggedIn, username, login, logout } }) ``` ## Store的使用 ### 在组件中使用Store ```vue <template> <div class="counter"> <h2>{{ name }}</h2> <p>Count: {{ count }}</p> <p>Double count: {{ doubleCount }}</p> <p>Double count + 5: {{ doubleCountPlus(5) }}</p> <button @click="increment">+1</button> <button @click="fetchCount">Fetch count</button> </div> </template> <script setup> import { storeToRefs } from 'pinia' import { useCounterStore } from '@/stores/counter' const store = useCounterStore() // 解构store时使用storeToRefs保持响应性 const { count, name, doubleCount } = storeToRefs(store) // actions可以直接解构 const { increment, fetchCount } = store // 访问带参数的getter const { doubleCountPlus } = store </script> ``` ### 多个Store协同工作 ```vue <template> <div class="user-dashboard"> <template v-if="isLoggedIn"> <h2>Welcome, {{ username }}!</h2> <div class="stats"> <p>Your points: {{ count }}</p> <button @click="increment">Earn point</button> </div> <button @click="handleLogout">Logout</button> </template> <template v-else> <button @click="handleLogin">Login</button> </template> </div> </template> <script setup> import { storeToRefs } from 'pinia' import { useUserStore } from '@/stores/user' import { useCounterStore } from '@/stores/counter' const userStore = useUserStore() const counterStore = useCounterStore() const { isLoggedIn, username } = storeToRefs(userStore) const { count } = storeToRefs(counterStore) const { increment } = counterStore const handleLogin = async () => { try { await userStore.login({ username: 'demo', password: '123456' }) } catch (error) { console.error('Login failed:', error) } } const handleLogout = () => { userStore.logout() // 重置计数器 counterStore.$reset() } </script> ``` ## Store的组织 ### 目录结构 ``` stores/ ├── index.ts # 导出所有store ├── modules/ # 按功能模块组织store │ ├── user.ts │ ├── cart.ts │ └── product.ts └── types.ts # TypeScript类型定义 ``` ### 类型定义 ```typescript // stores/types.ts export interface User { id: number name: string email: string avatar?: string } export interface UserState { user: User | null token: string | null } export interface CartItem { id: number name: string price: number quantity: number } export interface CartState { items: CartItem[] } ``` ### Store模块 ```typescript // stores/modules/cart.ts import { defineStore } from 'pinia' import type { CartState, CartItem } from '../types' export const useCartStore = defineStore('cart', { state: (): CartState => ({ items: [] }), getters: { totalItems: (state) => state.items.length, totalAmount: (state) => { return state.items.reduce((total, item) => { return total + item.price * item.quantity }, 0) } }, actions: { addItem(item: Omit<CartItem, 'quantity'>) { const existingItem = this.items.find(i => i.id === item.id) if (existingItem) { existingItem.quantity++ } else { this.items.push({ ...item, quantity: 1 }) } }, removeItem(itemId: number) { const index = this.items.findIndex(item => item.id === itemId) if (index > -1) { this.items.splice(index, 1) } }, updateQuantity(itemId: number, quantity: number) { const item = this.items.find(i => i.id === itemId) if (item) { item.quantity = Math.max(0, quantity) if (item.quantity === 0) { this.removeItem(itemId) } } }, clearCart() { this.items = [] } } }) ``` ## 最佳实践 ### 1. Store命名约定 ```typescript // 使用use前缀 export const useUserStore = defineStore('user', { // store实现 }) // 文件名与store名称保持一致 // stores/user.ts -> useUserStore // stores/shopping-cart.ts -> useShoppingCartStore ``` ### 2. 状态持久化 ```typescript import { defineStore } from 'pinia' export const useUserStore = defineStore('user', { state: () => ({ token: localStorage.getItem('token') || null }), actions: { setToken(token: string | null) { this.token = token if (token) { localStorage.setItem('token', token) } else { localStorage.removeItem('token') } } } }) ``` ### 3. 订阅状态变化 ```typescript import { watch } from 'vue' import { useCartStore } from '@/stores/cart' const cart = useCartStore() // 监听整个store watch( cart, (state) => { console.log('Cart state changed:', state) }, { deep: true } ) // 监听特定状态 watch( () => cart.items, (items) => { console.log('Cart items changed:', items) }, { deep: true } ) ``` ### 4. 错误处理 ```typescript import { defineStore } from 'pinia' export const useProductStore = defineStore('product', { state: () => ({ products: [], loading: false, error: null }), actions: { async fetchProducts() { this.loading = true this.error = null try { const response = await fetch('/api/products') if (!response.ok) { throw new Error('Failed to fetch products') } this.products = await response.json() } catch (error) { this.error = error.message throw error } finally { this.loading = false } } } }) ``` ## 总结 本文详细介绍了Pinia的Store创建和使用方法: 1. Pinia的基本概念和优势 2. Store的定义和配置 3. 在组件中使用Store 4. Store的组织和类型定义 5. 最佳实践和注意事项 通过使用Pinia,我们可以更简单、更高效地管理Vue应用的状态。建议在新项目中优先考虑使用Pinia而不是Vuex,因为它提供了更现代化的API和更好的开发体验。