Pinia 极简指南:Vue 3 官方状态管理库

0 阅读3分钟

这是一篇精简、易懂的 Pinia 入门整理,适合快速了解核心用法。

一、Pinia 是什么?

Pinia 是 Vue 生态的新一代状态管理库,已取代 Vuex 成为 Vue 3 的官方推荐方案。 它让你能在组件之间共享数据(状态),同时保持响应式、类型安全和开发工具友好。

一句话:Pinia = 更轻量、更直觉的 Vuex。


二、为什么选 Pinia,而不是 Vuex?

特性PiniaVuex 4
语法简洁没有 mutations,直接改状态必须通过 commit 提交 mutation                
TypeScript 支持               完美自动推断,无需额外类型声明                需要手动封装,体验较差
模块化天然的多个 store,无需嵌套模块需要 module 嵌套,命名空间复杂
体积~1 KB较大
开发工具Devtools 支持、热更新、时间旅行支持,但不如 Pinia 顺畅

核心理念变化

Pinia 不再区分 mutationsactions所有修改都直接通过 actions 或直接赋值完成,代码量明显减少。


三、核心概念

Pinia 的应用由多个独立的 Store 组成,每个 Store 就是一个独立的状态单元。

一个 Store 包含三部分:

  • State(状态) :存放数据的响应式对象,相当于组件的 data
  • Getters(计算属性) :基于 state 计算的派生值,相当于 computed
  • Actions(方法) :执行修改 state 的逻辑(同步或异步),相当于 methods

四、两种定义方式

选项式 Store(类似 Vue 2 写法)

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 函数)

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 一样,学习成本极低。


五、在组件中使用

<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 最佳实践

  1. 一个功能一个 Store
    不要把所有状态堆在一起,按业务或功能拆分成独立 store,如 useUserStoreuseCartStore

  2. 使用组合式 Store
    更灵活、更容易提取逻辑,也更符合 Vue 3 的组合式思维。

  3. 把异步请求放进 actions
    actions 支持 async/await,适合封装 API 调用并 update 状态。

  4. 利用 storeToRefs 保持解构响应性
    直接从 store 解构会失去响应式,需使用:

    import { storeToRefs } from 'pinia'
    const { count, double } = storeToRefs(counter)
    

    actions 可以直接解构,无需特殊处理。

  5. 注入全局 store 按需使用
    Pinia 无需在入口处挂载整个模块树,只需在 app.use(pinia) 创建实例后,随时在组件里调用 useXxxStore()


七、一个完整的小示例

stores/todo.js

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 }
})

组件中使用

<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 — 它会让你少写很多代码,思路也更清晰。