文章目录
- [Vue Pinia 状态管理实战指南](#Vue Pinia 状态管理实战指南)
-
- [为什么选择 Pinia?](#为什么选择 Pinia?)
- 基础使用
- 四个实战场景
-
- [1. 用户状态管理](#1. 用户状态管理)
- [2. 购物车功能](#2. 购物车功能)
- [3. 主题设置](#3. 主题设置)
- [4. API 数据管理](#4. API 数据管理)
- [Pinia vs Vuex 对比](#Pinia vs Vuex 对比)
- 最佳实践
-
- [1. Store 命名规范](#1. Store 命名规范)
- [2. 状态结构设计](#2. 状态结构设计)
- [3. 组合多个 Store](#3. 组合多个 Store)
- [4. 持久化存储](#4. 持久化存储)
- 常见问题
-
- [1. 状态重置](#1. 状态重置)
- [2. 监听状态变化](#2. 监听状态变化)
- [3. 服务端渲染 (SSR)](#3. 服务端渲染 (SSR))
- 总结
Vue Pinia 状态管理实战指南
轻量、直观、类型安全的 Vue 状态管理方案
为什么选择 Pinia?
Pinia 是 Vue 官方推荐的状态管理库,相比 Vuex 有以下优势:
- 更简单的 API:无需 mutations,直接修改状态
- 完美的 TypeScript 支持:天然类型推导
- 模块化设计:每个 store 都是独立的
- 开发工具友好:更好的调试体验
基础使用
安装和配置
bash
npm install pinia
javascript
// main.js
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const app = createApp(App)
app.use(createPinia())
app.mount('#app')
定义 Store
javascript
// stores/counter.js
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
name: 'Counter'
}),
getters: {
doubleCount: (state) => state.count * 2,
greeting: (state) => `Hello, ${state.name}!`
},
actions: {
increment() {
this.count++
},
async fetchData() {
// 异步操作
const data = await fetch('/api/data')
this.count = data.count
}
}
})
在组件中使用
vue
<template>
<div>
<p>计数: {{ counter.count }}</p>
<p>双倍: {{ counter.doubleCount }}</p>
<button @click="counter.increment()">增加</button>
</div>
</template>
<script setup>
import { useCounterStore } from '@/stores/counter'
const counter = useCounterStore()
</script>
四个实战场景
1. 用户状态管理
javascript
// stores/user.js
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
state: () => ({
user: null,
token: localStorage.getItem('token'),
isLoading: false
}),
getters: {
isAuthenticated: (state) => !!state.token,
userName: (state) => state.user?.name || '游客'
},
actions: {
async login(credentials) {
this.isLoading = true
try {
const response = await fetch('/api/login', {
method: 'POST',
body: JSON.stringify(credentials)
})
const data = await response.json()
this.user = data.user
this.token = data.token
localStorage.setItem('token', data.token)
} finally {
this.isLoading = false
}
},
logout() {
this.user = null
this.token = null
localStorage.removeItem('token')
}
}
})
2. 购物车功能
javascript
// stores/cart.js
import { defineStore } from 'pinia'
export const useCartStore = defineStore('cart', {
state: () => ({
items: []
}),
getters: {
totalItems: (state) => state.items.reduce((sum, item) => sum + item.quantity, 0),
totalPrice: (state) => state.items.reduce((sum, item) => sum + item.price * item.quantity, 0)
},
actions: {
addItem(product) {
const existingItem = this.items.find(item => item.id === product.id)
if (existingItem) {
existingItem.quantity++
} else {
this.items.push({ ...product, quantity: 1 })
}
},
removeItem(productId) {
const index = this.items.findIndex(item => item.id === productId)
if (index > -1) {
this.items.splice(index, 1)
}
},
clearCart() {
this.items = []
}
}
})
3. 主题设置
javascript
// stores/theme.js
import { defineStore } from 'pinia'
export const useThemeStore = defineStore('theme', {
state: () => ({
isDark: localStorage.getItem('theme') === 'dark'
}),
getters: {
theme: (state) => state.isDark ? 'dark' : 'light'
},
actions: {
toggleTheme() {
this.isDark = !this.isDark
localStorage.setItem('theme', this.theme)
document.documentElement.setAttribute('data-theme', this.theme)
},
setTheme(theme) {
this.isDark = theme === 'dark'
localStorage.setItem('theme', theme)
document.documentElement.setAttribute('data-theme', theme)
}
}
})
4. API 数据管理
javascript
// stores/posts.js
import { defineStore } from 'pinia'
export const usePostsStore = defineStore('posts', {
state: () => ({
posts: [],
loading: false,
error: null
}),
getters: {
publishedPosts: (state) => state.posts.filter(post => post.published),
getPostById: (state) => (id) => state.posts.find(post => post.id === id)
},
actions: {
async fetchPosts() {
this.loading = true
this.error = null
try {
const response = await fetch('/api/posts')
this.posts = await response.json()
} catch (error) {
this.error = error.message
} finally {
this.loading = false
}
},
async createPost(postData) {
const response = await fetch('/api/posts', {
method: 'POST',
body: JSON.stringify(postData)
})
const newPost = await response.json()
this.posts.push(newPost)
}
}
})
Pinia vs Vuex 对比
| 特性 | Pinia | Vuex |
|---|---|---|
| API 复杂度 | 简单直观 | 相对复杂 |
| TypeScript | 原生支持 | 需要额外配置 |
| 模块化 | 天然模块化 | 需要 modules |
| 状态修改 | 直接修改 | 必须通过 mutations |
| 异步操作 | actions 中直接处理 | 需要 actions + mutations |
javascript
// Pinia - 简洁直观
const store = useStore()
store.count++
store.updateUser(userData)
// Vuex - 需要 commit
store.commit('INCREMENT')
store.dispatch('updateUser', userData)
最佳实践
1. Store 命名规范
javascript
// 推荐:use + 功能名 + Store
export const useUserStore = defineStore('user', {})
export const useCartStore = defineStore('cart', {})
export const useThemeStore = defineStore('theme', {})
2. 状态结构设计
javascript
// 推荐:扁平化状态结构
state: () => ({
user: null,
isLoading: false,
error: null
})
// 避免:过度嵌套
state: () => ({
user: {
profile: {
personal: {
name: ''
}
}
}
})
3. 组合多个 Store
vue
<script setup>
import { useUserStore } from '@/stores/user'
import { useCartStore } from '@/stores/cart'
const userStore = useUserStore()
const cartStore = useCartStore()
// 可以在 actions 中调用其他 store
const handlePurchase = () => {
if (userStore.isAuthenticated) {
cartStore.clearCart()
}
}
</script>
4. 持久化存储
javascript
// 简单的持久化实现
export const useSettingsStore = defineStore('settings', {
state: () => ({
language: 'zh-CN',
notifications: true
}),
actions: {
updateSettings(settings) {
Object.assign(this, settings)
localStorage.setItem('settings', JSON.stringify(this.$state))
},
loadSettings() {
const saved = localStorage.getItem('settings')
if (saved) {
Object.assign(this, JSON.parse(saved))
}
}
}
})
常见问题
1. 状态重置
javascript
// 重置整个 store
const store = useStore()
store.$reset()
// 重置特定状态
store.$patch({
count: 0,
name: ''
})
2. 监听状态变化
javascript
// 在组件中监听
import { watch } from 'vue'
const store = useStore()
watch(
() => store.count,
(newCount) => {
console.log('Count changed:', newCount)
}
)
3. 服务端渲染 (SSR)
javascript
// 在 SSR 中使用
export const useStore = defineStore('main', {
state: () => ({
data: null
}),
actions: {
async hydrate() {
if (process.client && !this.data) {
await this.fetchData()
}
}
}
})
总结
Pinia 的核心优势:
- 简单易用:API 设计直观,学习成本低
- 类型安全:完美的 TypeScript 支持
- 性能优秀:按需响应,避免不必要的更新
- 开发体验:优秀的开发工具支持
- 渐进式:可以逐步迁移现有项目
选择 Pinia,让 Vue 状态管理变得更加简单高效!
希望这篇指南能帮你快速上手 Pinia 状态管理!