这是一篇精简、易懂的 Pinia 入门整理,适合快速了解核心用法。
一、Pinia 是什么?
Pinia 是 Vue 生态的新一代状态管理库,已取代 Vuex 成为 Vue 3 的官方推荐方案。 它让你能在组件之间共享数据(状态),同时保持响应式、类型安全和开发工具友好。
一句话:Pinia = 更轻量、更直觉的 Vuex。
二、为什么选 Pinia,而不是 Vuex?
| 特性 | Pinia | Vuex 4 |
|---|---|---|
| 语法简洁 | 没有 mutations,直接改状态 |
必须通过 commit 提交 mutation |
| TypeScript 支持 | 完美自动推断,无需额外类型声明 | 需要手动封装,体验较差 |
| 模块化 | 天然的多个 store,无需嵌套模块 | 需要 module 嵌套,命名空间复杂 |
| 体积 | ~1 KB | 较大 |
| 开发工具 | Devtools 支持、热更新、时间旅行 | 支持,但不如 Pinia 顺畅 |
核心理念变化:
Pinia 不再区分
mutations与actions--- 所有修改都直接通过actions或直接赋值完成,代码量明显减少。
三、核心概念
Pinia 的应用由多个独立的 Store 组成,每个 Store 就是一个独立的状态单元。
一个 Store 包含三部分:
- State(状态) :存放数据的响应式对象,相当于组件的
data - Getters(计算属性) :基于 state 计算的派生值,相当于
computed - Actions(方法) :执行修改 state 的逻辑(同步或异步),相当于
methods
四、两种定义方式
选项式 Store(类似 Vue 2 写法)
javascript
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0
}),
getters: {
double: (state) => state.count * 2
},
actions: {
increment() {
this.count++
}
}
})
组合式 Store(推荐,更像 Vue 3 的 setup 函数)
javascript
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const double = computed(() => count.value * 2)
function increment() {
count.value++
}
return { count, double, increment }
})
组合式写法让你可以复用逻辑,和写一个普通 composable 一样,学习成本极低。
五、在组件中使用
vue
<script setup>
import { useCounterStore } from '@/stores/counter'
const counter = useCounterStore()
// 访问 state
console.log(counter.count)
// 读取 getter
console.log(counter.double)
// 调用 action
counter.increment()
// 直接修改 state(允许,但推荐在 action 中集中处理)
counter.count++
</script>
注意 :直接修改
state在 Pinia 中是完全合法的,不会破坏响应式。但为了可维护性,通常建议将修改逻辑封装在action里。
六、Pinia 最佳实践
-
一个功能一个 Store
不要把所有状态堆在一起,按业务或功能拆分成独立 store,如
useUserStore、useCartStore。 -
使用组合式 Store
更灵活、更容易提取逻辑,也更符合 Vue 3 的组合式思维。
-
把异步请求放进 actions
actions 支持 async/await,适合封装 API 调用并 update 状态。
-
利用
storeToRefs保持解构响应性直接从 store 解构会失去响应式,需使用:
javascriptimport { storeToRefs } from 'pinia' const { count, double } = storeToRefs(counter)而
actions可以直接解构,无需特殊处理。 -
注入全局 store 按需使用
Pinia 无需在入口处挂载整个模块树,只需在
app.use(pinia)创建实例后,随时在组件里调用useXxxStore()。
七、一个完整的小示例
stores/todo.js
javascript
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
export const useTodoStore = defineStore('todo', () => {
const list = ref([])
const filter = ref('all')
const filteredList = computed(() => {
if (filter.value === 'done') return list.value.filter(t => t.done)
if (filter.value === 'todo') return list.value.filter(t => !t.done)
return list.value
})
function addTodo(text) {
list.value.push({ id: Date.now(), text, done: false })
}
function toggle(id) {
const todo = list.value.find(t => t.id === id)
if (todo) todo.done = !todo.done
}
return { list, filter, filteredList, addTodo, toggle }
})
组件中使用
vue
<template>
<input v-model="newTodo" @keyup.enter="add" />
<ul>
<li v-for="t in todoStore.filteredList" :key="t.id">
<input type="checkbox" :checked="t.done" @change="todoStore.toggle(t.id)" />
{{ t.text }}
</li>
</ul>
</template>
<script setup>
import { ref } from 'vue'
import { useTodoStore } from '@/stores/todo'
const todoStore = useTodoStore()
const newTodo = ref('')
function add() {
if (newTodo.value.trim()) {
todoStore.addTodo(newTodo.value)
newTodo.value = ''
}
}
</script>
八、总结
- Pinia 是 Vue 官方指定的下一代状态管理库,更简洁、更灵活、TypeScript 更友好。
- 核心就是通过
defineStore定义一个状态单元,组件中直接调用即可。 - 推荐使用 组合式语法,将状态、计算属性和方法像写 composable 一样组织。
- 没有 mutation,没有模块嵌套,只有你需要的功能。
若你正从 Vuex 迁移,或者直接在 Vue 3 项目中使用状态管理,大胆选择 Pinia --- 它会让你少写很多代码,思路也更清晰。