1、Pinia介绍
Pinia 是 Vue 官方推荐的状态管理库,由 Vuex 团队核心成员开发,用于替代 Vuex(Vue 3 已将 Pinia 作为官方状态管理方案)。它专为 Vue 2 和 Vue 3 设计,API 简洁直观,且原生支持 TypeScript,是处理跨组件/页面共享状态的最佳选择。
Pinia 的核心特点
- 简化的 API:移除了 Vuex 中的
mutations(仅保留state、getters、actions),减少模板代码。 - TypeScript 友好:天生支持类型推断,无需手动定义类型。
- 多 Store 支持:可以创建多个独立的 store,避免单一状态树的臃肿。
- 响应式集成:状态自动具备响应式,直接在组件中使用即可触发更新。
- 异步支持:
actions可直接处理异步逻辑(无需像 Vuex 那样区分actions和mutations)。 - 代码分割:自动拆分打包,仅加载用到的 store。
Pinia 核心概念
Pinia 的这几个核心概念是状态管理的基础,我们可以结合 Vue 组件的特性来类比理解,让概念更直观:
1. Store(状态容器)
- 定义:Store 是 Pinia 中最核心的概念,本质是一个「状态容器」,用于集中管理应用中需要跨组件共享的数据(比如用户信息、购物车数据等)。
- 特点:
- 每个 Store 必须通过
defineStore函数定义,且需要一个唯一的 ID(字符串),这个 ID 是 Pinia 区分不同 Store 的标识(类似组件的name属性)。 - 一个应用可以有多个 Store,按业务模块拆分(比如
userStore管理用户状态、cartStore管理购物车),避免像 Vuex 那样所有状态挤在一个「单一状态树」里。
- 每个 Store 必须通过
- 类比:可以理解为一个「全局的组件」,但它不负责渲染 UI,只负责管理数据和逻辑。
2. State(状态数据)
- 定义:State 是 Store 中存储原始数据的地方,相当于组件中的
data选项。 - 特点:
- 是响应式的:当 State 中的数据发生变化时,所有使用该数据的组件会自动更新(和 Vue 组件的响应式原理一致)。
- 定义方式:通过函数返回一个对象(避免在服务端渲染时出现状态污染),例如
state: () => ({ count: 0, user: null })。
- 作用:保存应用的「源数据」,是整个状态管理的「数据源」。
- 类比:就像组件里的
data()返回的对象,只不过这些数据可以被整个应用的任何组件访问和修改。
3. Getters(派生状态)
- 定义:Getters 用于基于 State 计算派生状态,相当于组件中的
computed计算属性。 - 特点:
- 缓存机制:Getters 的返回值会被缓存,只有当它依赖的 State 发生变化时,才会重新计算(和
computed一样,提高性能)。 - 支持依赖其他 Getters:可以在一个 Getters 中调用另一个 Getters(通过第二个参数
getters访问)。 - 可以传参:通过返回一个函数实现,例如
getters: { getUserById: (state) => (id) => state.users.find(u => u.id === id) }。
- 缓存机制:Getters 的返回值会被缓存,只有当它依赖的 State 发生变化时,才会重新计算(和
- 作用:对 State 进行加工处理,得到新的状态(比如从列表中筛选数据、计算总和等)。
- 类比:类似组件中
computed,从现有数据中「计算」出新数据,且自动追踪依赖。
4. Actions(状态修改逻辑)
- 定义:Actions 是 Store 中定义方法的地方,用于修改 State 或处理业务逻辑,相当于组件中的
methods。 - 特点:
- 支持同步和异步:可以直接写异步逻辑(比如调用 API、定时器等),无需像 Vuex 那样区分
actions(异步)和mutations(同步)。 - 直接修改 State:在 Actions 中可以通过
this直接访问和修改 State(例如this.count++)。 - 可复用逻辑:多个组件可以调用同一个 Action,实现逻辑复用。
- 可以调用其他 Actions:通过
this访问当前 Store 的其他 Actions(this.otherAction())。
- 支持同步和异步:可以直接写异步逻辑(比如调用 API、定时器等),无需像 Vuex 那样区分
- 作用:封装修改状态的逻辑(避免在组件中直接修改 State 导致逻辑分散),尤其是复杂逻辑或异步操作。
- 类比:类似组件中的
methods,但它的操作对象是全局的 State,而不是组件内部的data。
总结关系
- Store 是容器,包含了 State(数据)、Getters(计算数据)、Actions(操作数据的方法)。
- 组件通过调用 Store 的 Actions 修改 State,通过访问 State 或 Getters 获取数据,从而实现跨组件的状态共享和联动。
使用示例
下面通过两个场景演示 Pinia 的用法:基础计数器、异步获取用户信息。
1. 安装 Pinia
# Vue 3 项目
npm install pinia
# 或
yarn add pinia
2. 初始化 Pinia
在入口文件(如 main.js)中创建 Pinia 实例并挂载到 Vue 应用:
// main.js
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const app = createApp(App)
const pinia = createPinia() // 创建 Pinia 实例
app.use(pinia) // 挂载到应用
app.mount('#app')
3. 示例 1:基础计数器 Store
创建一个管理计数器状态的 store,包含递增、递减功能。
定义 Store
新建 stores/counter.js:
import { defineStore } from 'pinia'
// 用 defineStore 定义 store,第一个参数是唯一 ID(必须)
export const useCounterStore = defineStore('counter', {
// 状态(初始数据)
state: () => ({
count: 0,
title: '计数器'
}),
// 计算属性(派生状态)
getters: {
// 带参数的 getter(需返回函数)
doubleCount: (state) => state.count * 2,
// 依赖其他 getter
doubleCountPlusOne: (state, getters) => getters.doubleCount + 1
},
// 方法(修改状态)
actions: {
increment() {
// 直接修改 state(Pinia 会自动追踪)
this.count++
},
decrement() {
this.count--
},
// 带参数的 action
incrementBy(num) {
this.count += num
}
}
})
在组件中使用 Store
在任意组件中导入并使用上述 store:
<!-- CounterComponent.vue -->
<template>
<div>
<h2>{{ counterStore.title }}</h2>
<p>当前值:{{ counterStore.count }}</p>
<p>翻倍后:{{ counterStore.doubleCount }}</p>
<p>翻倍+1:{{ counterStore.doubleCountPlusOne }}</p>
<button @click="counterStore.increment">+1</button>
<button @click="counterStore.decrement">-1</button>
<button @click="counterStore.incrementBy(5)">+5</button>
</div>
</template>
<script setup>
// 导入 store
import { useCounterStore } from '@/stores/counter'
// 获取 store 实例(响应式)
const counterStore = useCounterStore()
</script>
4. 示例 2:异步数据 Store
创建一个获取用户信息的 store,演示 actions 处理异步逻辑。
定义 Store
新建 stores/user.js:
import { defineStore } from 'pinia'
import axios from 'axios' // 假设用 axios 请求接口
export const useUserStore = defineStore('user', {
state: () => ({
userInfo: null, // 用户信息
loading: false, // 加载状态
error: null // 错误信息
}),
getters: {
// 派生用户名称(处理 null 情况)
userName: (state) => state.userInfo?.name || '未知用户'
},
actions: {
// 异步获取用户信息(支持 async/await)
async fetchUserInfo(userId) {
try {
this.loading = true
this.error = null
// 发起请求
const res = await axios.get(`/api/users/${userId}`)
// 修改状态
this.userInfo = res.data
} catch (err) {
this.error = '获取用户信息失败'
console.error(err)
} finally {
this.loading = false
}
},
// 清除用户信息
clearUser() {
this.userInfo = null
}
}
})
在组件中使用异步 Store
<!-- UserComponent.vue -->
<template>
<div>
<button @click="fetchUser">获取用户信息</button>
<button @click="userStore.clearUser">清除</button>
<div v-if="userStore.loading">加载中...</div>
<div v-if="userStore.error" style="color: red">{{ userStore.error }}</div>
<div v-if="userStore.userInfo">
<p>姓名:{{ userStore.userName }}</p>
<p>年龄:{{ userStore.userInfo.age }}</p>
</div>
</div>
</template>
<script setup>
import { useUserStore } from '@/stores/user'
const userStore = useUserStore()
// 调用异步 action
const fetchUser = () => {
userStore.fetchUserInfo(123) // 假设用户 ID 为 123
}
</script>
总结
Pinia 相比 Vuex 更简洁、灵活,通过 state 存储数据,getters 处理计算逻辑,actions 处理同步/异步操作,且天然支持 TypeScript 和 Vue 3 的 Composition API。实际开发中,建议按业务模块拆分多个 store(如 userStore、cartStore),使状态管理更清晰。
3119

被折叠的 条评论
为什么被折叠?



