1. 为什么选择 Pinia 作为 Vue3 的状态管理方案
在 Vue3 生态中,状态管理一直是个绕不开的话题。相比 Vuex,Pinia 提供了更符合 Vue3 组合式 API 思维的状态管理方式。我在多个实际项目中对比使用后发现,Pinia 的 API 设计更加简洁直观,完全拥抱了 Composition API 的特性,不需要像 Vuex 那样区分 mutations 和 actions,所有状态变更都在同一个地方处理。
Pinia 的核心优势在于:
- 完整的 TypeScript 支持,类型推断非常友好
- 去除了 mutations 的概念,所有状态变更都通过 actions 处理
- 支持多个 store 的自动代码分割
- 体积更小(压缩后约 1KB)
- 与 Vue DevTools 完美集成
提示:如果你是从 Vuex 迁移过来的开发者,可以简单理解为 Pinia = Vuex 去掉 mutations + 更好的 TS 支持 + 更简洁的 API。
2. Pinia 基础架构搭建
2.1 初始化 Pinia 实例
首先需要在项目中安装 Pinia:
bash复制npm install pinia
# 或者
yarn add pinia
然后创建 Pinia 实例并挂载到 Vue 应用:
javascript复制// stores/index.js
import { createPinia } from 'pinia'
const pinia = createPinia()
export default pinia
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import pinia from '@/stores'
const app = createApp(App)
app.use(pinia)
app.mount('#app')
这种架构设计将 Pinia 的初始化逻辑集中管理,便于后续扩展。比如你可能需要添加一些插件:
javascript复制// stores/index.js
import { createPinia } from 'pinia'
const pinia = createPinia()
// 添加持久化插件
pinia.use((context) => {
// 插件逻辑
})
export default pinia
2.2 Store 的基本结构
Pinia 的 Store 使用 defineStore 函数定义,支持两种风格:
- Options 风格(类似 Vue 的选项式 API)
- Setup 风格(类似组合式 API)
我强烈推荐使用 Setup 风格,因为它能充分发挥 Composition API 的优势:
javascript复制// stores/user.js
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
export const useUserStore = defineStore('user', () => {
// 状态定义
const userInfo = ref({})
const isLoggedIn = ref(false)
const permissions = ref([])
// 计算属性
const hasPermission = computed(() => {
return (permission) => {
return permissions.value.includes(permission)
}
})
// 动作方法
const setUser = (user) => {
userInfo.value = user
isLoggedIn.value = !!user
}
// 其他方法...
return {
userInfo,
isLoggedIn,
permissions,
hasPermission,
setUser
// ...
}
})
3. 用户状态管理实战
3.1 用户认证状态管理
用户认证是大多数应用的核心功能,我们需要管理:
- 用户基本信息
- 登录状态
- 权限信息
javascript复制// stores/user.js
export const useUserStore = defineStore('user', () => {
// 初始状态
const userInfo = ref(null)
const isLoggedIn = ref(false)
const permissions = ref([])
// 登录动作
const login = async (credentials) => {
try {
const response = await api.login(credentials)
userInfo.value = response.user
permissions.value = response.permissions
isLoggedIn.value = true
return response
} catch (error) {
clearUser()
throw error
}
}
// 登出动作
const logout = async () => {
await api.logout()
clearUser()
}
// 清除用户状态
const clearUser = () => {
userInfo.value = null
isLoggedIn.value = false
permissions.value = []
}
// 检查权限
const hasPermission = computed(() => {
return (permission) => permissions.value.includes(permission)
})
return {
userInfo,
isLoggedIn,
permissions,
login,
logout,
hasPermission
}
})
3.2 在组件中使用用户 Store
vue复制<template>
<div class="user-profile">
<h2>用户信息</h2>
<p v-if="!userStore.isLoggedIn">请先登录</p>
<template v-else>
<p>姓名: {{ userStore.userInfo.name }}</p>
<p>邮箱: {{ userStore.userInfo.email }}</p>
<button
v-if="userStore.hasPermission('user.edit')"
@click="editProfile"
>
编辑资料
</button>
<button @click="logout">退出登录</button>
</template>
</div>
</template>
<script setup>
import { useUserStore } from '@/stores/user'
const userStore = useUserStore()
const editProfile = () => {
// 编辑逻辑
}
const logout = () => {
userStore.logout()
router.push('/login')
}
</script>
4. 应用全局状态管理
4.1 管理 UI 状态
除了用户状态,我们还需要管理应用的 UI 状态:
javascript复制// stores/app.js
export const useAppStore = defineStore('app', () => {
// 侧边栏状态
const sidebar = reactive({
opened: true,
withoutAnimation: false
})
// 设备类型
const device = ref('desktop')
// 全屏状态
const fullscreen = ref(false)
// 响应式窗口大小
const windowSize = reactive({
width: window.innerWidth,
height: window.innerHeight
})
// 监听窗口变化
const handleResize = () => {
win
解锁全文
加入我们的会员,获取最新、最热、最精彩的开发者技术内容