
轻量、直观、类型安全的 Vue 状态管理方案
Pinia 是 Vue 官方推荐的状态管理库,相比 Vuex 有以下优势:
npm install pinia// 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')// 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
}
}
})<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>// 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')
}
}
})// 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 = []
}
}
})// 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)
}
}
})// 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 | Vuex |
|---|---|---|
API 复杂度 | 简单直观 | 相对复杂 |
TypeScript | 原生支持 | 需要额外配置 |
模块化 | 天然模块化 | 需要 modules |
状态修改 | 直接修改 | 必须通过 mutations |
异步操作 | actions 中直接处理 | 需要 actions + mutations |
// Pinia - 简洁直观
const store = useStore()
store.count++
store.updateUser(userData)
// Vuex - 需要 commit
store.commit('INCREMENT')
store.dispatch('updateUser', userData)// 推荐:use + 功能名 + Store
export const useUserStore = defineStore('user', {})
export const useCartStore = defineStore('cart', {})
export const useThemeStore = defineStore('theme', {})// 推荐:扁平化状态结构
state: () => ({
user: null,
isLoading: false,
error: null
})
// 避免:过度嵌套
state: () => ({
user: {
profile: {
personal: {
name: ''
}
}
}
})<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>// 简单的持久化实现
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))
}
}
}
})// 重置整个 store
const store = useStore()
store.$reset()
// 重置特定状态
store.$patch({
count: 0,
name: ''
})// 在组件中监听
import { watch } from 'vue'
const store = useStore()
watch(
() => store.count,
(newCount) => {
console.log('Count changed:', newCount)
}
)// 在 SSR 中使用
export const useStore = defineStore('main', {
state: () => ({
data: null
}),
actions: {
async hydrate() {
if (process.client && !this.data) {
await this.fetchData()
}
}
}
})Pinia 的核心优势:
选择 Pinia,让 Vue 状态管理变得更加简单高效!
希望这篇指南能帮你快速上手 Pinia 状态管理!