Vue3 状态管理 Pinia 入门指南

 更新时间:2026年05月24日 09:37:55   作者:叫我少年  
Pinia 是 Vue 3 官方推荐的状态管理库,相较于 Vuex 更加轻量、易于使用,下面就来详细的介绍一下Vue3 状态管理 Pinia 入门,感兴趣的可以了解一下

很多刚接触 Vue3 状态管理的同学都会问:“Vuex 还没学明白,怎么又出了个 Pinia?”这篇就来聊聊 Pinia,看看它到底是什么、为什么要用它、以及怎么用好它。

  1. Pinia 是什么:Pinia 是 Vue 的全新状态管理库,它和 Vuex 有哪些核心区别?
  2. 如何定义 Store:掌握选项式和组合式两种创建 Store 的风格。
  3. 在组件中如何使用:学会在 setup 中正确地使用 Store 并保持响应性。

一、使用场景

Pinia 适用于需要对 Vue 应用进行全局状态管理的场景:

  • 跨组件共享状态:例如用户登录信息、购物车数据、主题配置等
  • 与后端 API 交互:集中管理 API 调用和数据缓存逻辑
  • 调试与开发体验:配合 Vue DevTools 进行状态追踪和时间旅行
  • 类型安全:为 TypeScript 项目提供完整的类型推导支持
  • 模块化管理:将不同领域的全局状态拆分为独立的 Store,易于维护和扩展

二、注意事项

划重点: Pinia 在 Vue 2 和 Vue 3 中均可使用,但本文所有示例均基于 Vue 3 + Script Setup 语法。

  • defineStore的 ID 必须唯一:每个 Store 都需要一个唯一的 ID,用于 DevTools 的连接
  • 没有 mutations​:与 Vuex 不同,Pinia 将状态(state​)与变更逻辑(actions)直接统一,摒弃了繁琐的 mutations
  • 避免过度使用全局状态:组件内部能处理的数据,不要提升到 Store 中

三、基本用法

3.1 安装与配置

yarn add pinia
# 或
npm install pinia

main.ts 中创建 Pinia 实例并注册到 Vue 应用:

// main.ts
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'

const pinia = createPinia()
const app = createApp(App)

app.use(pinia)
app.mount('#app')

3.2 定义 Store

Pinia 提供两种风格来定义 Store:选项式(Options API)组合式(Composition API) 。选择哪个全凭个人喜好,但组合式风格更适合复杂逻辑的复用。

选项式风格:

// stores/counter.ts
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', {
  // 状态(数据)
  state: () => ({
    count: 0,
    name: '我的计数器',
  }),
  // 计算属性(派生数据)
  getters: {
    // 使用箭头函数时,state 为第一个参数
    doubleCount: (state) => state.count * 2,
    // 需要访问其他 getter 时,使用普通函数
    doubleCountPlusOne(): number {
      return this.doubleCount + 1
    },
  },
  // 操作方法(业务逻辑)
  actions: {
    increment() {
      this.count++
    },
    decrement() {
      this.count--
    },
    // 支持异步操作
    async incrementAsync() {
      await new Promise((resolve) => setTimeout(resolve, 1000))
      this.increment()
    },
    incrementBy(amount: number) {
      this.count += amount
    },
  },
})

组合式风格(推荐):

// stores/user.ts
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import { api } from '@/services/api' // 假设的 API 服务

// 类型定义
interface User {
  id: number
  name: string
  email: string
}
interface LoginCredentials {
  username: string
  password: string
}

export const useUserStore = defineStore('user', () => {
  // 状态(类似 options 的 data)
  const user = ref<User | null>(null)
  const isLoggedIn = ref(false)

  // 计算属性(类似 options 的 getters)
  const userName = computed(() => user.value?.name || '游客')
  const userId = computed(() => user.value?.id || 0)

  // 操作方法(类似 options 的 actions)
  function login(credentials: LoginCredentials) {
    return api.login(credentials).then((response) => {
      user.value = response.user
      isLoggedIn.value = true
      return response
    })
  }

  function logout() {
    user.value = null
    isLoggedIn.value = false
  }

  async function fetchUserProfile() {
    if (!userId.value) return
    try {
      const profile = await api.getUserProfile(userId.value)
      user.value = { ...user.value, ...profile }
    } catch (error) {
      console.error('获取用户资料失败:', error)
    }
  }

  // 返回需要暴露给外部的所有内容
  return { user, isLoggedIn, userName, userId, login, logout, fetchUserProfile }
})

代码解析:

  1. ​​defineStore​:这是定义 Store 的核心函数,第一个参数是 Store 的唯一 ID,第二个参数是 Store 的定义(可以是对象或函数)。
  2. ​​ref​​ 和 ​computed​​:在组合式风格中,我们使用 Vue 的响应式 API 来定义状态和计算属性,和写组件 script setup 一模一样。
  3. ​​return​​ 语句:组合式 Store 必须 return 一个对象,对象中的属性和方法才能被外部访问。

3.3 在组件中使用 Store

<template>
  <div>
    <h1>{{ counterStore.name }}</h1>
    <p>计数: {{ counterStore.count }}</p>
    <p>双倍计数: {{ counterStore.doubleCount }}</p>
    <p>用户名: {{ userStore.userName }}</p>

    <button @click="counterStore.increment()">增加</button>
    <button @click="counterStore.decrement()">减少</button>
    <button @click="counterStore.incrementBy(5)">增加5</button>
    <button @click="counterStore.incrementAsync()">异步增加</button>

    <button @click="login" v-if="!userStore.isLoggedIn">登录</button>
    <button @click="userStore.logout()" v-else>退出</button>
  </div>
</template>

<script lang="ts" setup>
import { watch } from 'vue'
import { useCounterStore } from '@/stores/counter'
import { useUserStore } from '@/stores/user'
import { storeToRefs } from 'pinia'

const counterStore = useCounterStore()
const userStore = useUserStore()

// 关键:使用 storeToRefs 保持解构后的响应性
const { count, doubleCount } = storeToRefs(counterStore)
const { userName, isLoggedIn } = storeToRefs(userStore)

// 直接调用 action(action 本身就是方法,无需解构)
const login = () => {
  userStore
    .login({ username: 'admin', password: 'password' })
    .then(() => {
      console.log('登录成功')
    })
    .catch((error) => {
      console.error('登录失败:', error)
    })
}

// 监听 Store 状态变化
watch(
  () => counterStore.count,
  (newCount, oldCount) => {
    console.log(`计数从 ${oldCount} 变为 ${newCount}`)
  }
)
</script>

代码解析:

  1. ​​storeToRefs​​:这是 Pinia 提供的一个工具函数,用于“解包” Store 的 state 和 getters。直接从 Store 解构 const { count } = counterStore​ 会失去响应性,而 storeToRefs​ 保证了解构后的每个 ref 依然是响应式的。
  2. 直接调用 ​actions​​:actions​ 是普通函数,可以直接从 Store 对象上调用,不需要解构。counterStore.increment()​ 和 userStore.login() 都是正确的用法。
  3. ​​watch​​ 监听:你可以像监听普通 ref​ 一样监听 Store 中的状态,使用 watch(() => counterStore.count, handler)。

刚接触 Pinia,建议先从最典型的状态(比如用户登录信息、全局配置)开始使用,不要一开始就把所有组件数据都放进 Store。写得顺手了,再逐步将那些频繁跨组件传递的 props 和 emit 改为 Store 管理,你会发现代码瞬间清爽许多。

到此这篇关于Vue3 状态管理 Pinia 入门指南的文章就介绍到这了,更多相关Vue3 状态管理 Pinia内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!‍

相关文章

  • 基于ant design日期控件使用_仅月份的操作

    基于ant design日期控件使用_仅月份的操作

    这篇文章主要介绍了基于ant design日期控件使用_仅月份的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-10-10
  • 在Vue中使用Echarts实例图的方法实例

    在Vue中使用Echarts实例图的方法实例

    这篇文章主要给大家介绍了关于如何在Vue中使用Echarts实例图的相关资料,文中介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-10-10
  • vue中如何使用math.js

    vue中如何使用math.js

    这篇文章主要介绍了vue中如何使用math.js问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-05-05
  • 在Vue+SpringBoot应用中实现导入导出excel表功能全过程

    在Vue+SpringBoot应用中实现导入导出excel表功能全过程

    这篇文章主要介绍了如何在Vue+SpringBoot应用中实现Excel导入导出功能,前端分三步下载模板、上传文件、调用接口,后端通过FastExcel读取数据并调用UserDao插入,使用/user/importUsers接口完成数据交互,需要的朋友可以参考下
    2025-10-10
  • Vue中map()的用法案例

    Vue中map()的用法案例

    map()函数定义在JS的array中,它返回一个新的数组,下面这篇文章主要给大家介绍了关于Vue中map()的用法案例,文中通过示例代码介绍的非常详细,需要的朋友可以参考下
    2022-07-07
  • react+vite动态导入报错@vite-ignore的问题及解决

    react+vite动态导入报错@vite-ignore的问题及解决

    这篇文章主要介绍了react+vite动态导入报错@vite-ignore的问题及解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-03-03
  • vue.js todolist实现代码

    vue.js todolist实现代码

    这篇文章主要介绍了vue.js todolist实现代码,需要的朋友可以参考下
    2017-10-10
  • antd form表单数据回显操作

    antd form表单数据回显操作

    这篇文章主要介绍了antd form表单数据回显操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-11-11
  • vue实现实时上传文件进度条

    vue实现实时上传文件进度条

    这篇文章主要为大家详细介绍了vue实现实时上传文件进度条,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-03-03
  • 关于Element table组件滚动条不显示的踩坑记录

    关于Element table组件滚动条不显示的踩坑记录

    这篇文章主要介绍了关于Element table组件滚动条不显示的踩坑记录,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-08-08

最新评论