Vue Pinia 状态管理实战指南

文章目录

  • [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 的核心优势:

  1. 简单易用:API 设计直观,学习成本低
  2. 类型安全:完美的 TypeScript 支持
  3. 性能优秀:按需响应,避免不必要的更新
  4. 开发体验:优秀的开发工具支持
  5. 渐进式:可以逐步迁移现有项目

选择 Pinia,让 Vue 状态管理变得更加简单高效!


希望这篇指南能帮你快速上手 Pinia 状态管理!

相关推荐
绝无仅有4 小时前
某教育大厂面试题解析:MySQL索引、Redis缓存、Dubbo负载均衡等
vue.js·后端·面试
sean4 小时前
开发一个自己的 claude code
前端·后端·ai编程
用户21411832636024 小时前
dify案例分享-用 Dify 一键生成教学动画 HTML!AI 助力,3 分钟搞定专业级课件
前端
没逛够4 小时前
Vue 自适应高度表格
javascript·vue.js·elementui
搬砖的小码农_Sky5 小时前
Linux(Ubuntu)操作系统下文件的解压
linux·ubuntu
太过平凡的小蚂蚁6 小时前
Kotlin 协程中常见的异步返回与控制方式(速览)
开发语言·前端·kotlin
苹果醋36 小时前
JAVA面试汇总(二)多线程(五)
运维·vue.js·spring boot·nginx·课程设计
咖啡の猫7 小时前
Vue初始化脚手架
前端·javascript·vue.js
一 乐7 小时前
汽车销售|汽车推荐|基于SprinBoot+vue的新能源汽车个性化推荐系统(源码+数据库+文档)
java·数据库·vue.js·汽车·毕设·汽车个性化推荐