Pinia 状态管理原理与实战全解析

一、前言:为什么选择 Pinia?

在 Vue2 时代,我们常用 Vuex 来做全局状态管理。

但是 Vue3 带来了全新的响应式系统(Composition API + Proxy),于是 Vue 官方团队推出了 Pinia ------ 一款更轻量、更现代、更易用的状态管理库。

Pinia 的核心理念是:

"让状态管理像使用普通变量一样简单。"

相比 Vuex,它具备以下优势:

特点 Vuex Pinia
语法 基于 Mutations/Actions 直接使用函数
类型推导 较弱 TypeScript 支持友好
响应式实现 Object.defineProperty Vue3 Proxy
模块化 需手动命名空间 天然支持模块化
体积 较大 小巧轻量(约1KB)

二、Pinia 的核心原理

要理解 Pinia,先要知道它是如何在底层维持响应式的。

1. 状态的本质:Reactive

Pinia 使用 Vue3 的 reactive() 来存储状态。

每个 store 内部其实就是一个被 reactive 包裹的对象。

javascript 复制代码
import { reactive } from 'vue'

const state = reactive({
  count: 0
})

当 state.count++ 改变时,所有引用它的组件都会自动更新。

这就是 Pinia 响应式的根本机制。

2. Getter 的本质:Computed

在 Pinia 中,getter 相当于 Vue 中的 computed。

css 复制代码
getters: {
  doubleCount: (state) => state.count * 2
}

Pinia 会在内部把它转成一个 计算属性,只有依赖变化时才会重新计算。

因此它是 响应式、缓存式 的。

3. Action 的本质:普通函数 + 作用域代理

Pinia 不再强制使用 Mutation。

Action 其实就是对状态修改的封装函数。

javascript 复制代码
actions: {
  increment() {
    this.count++
  }
}

Pinia 通过 Proxy 让 this 指向 store 实例,因此你可以像访问普通对象一样修改状态。

4. 模块与依赖收集机制

每一个 store 都是一个独立的响应式作用域(Reactive Scope)。

Pinia 会将它注册到全局的 store 容器中(类似一个 Map 结构),并在组件使用时完成依赖收集。

组件一旦引用某个 store 的状态,Pinia 就会追踪它的依赖关系。

当 store 内的状态改变时,Pinia 会自动触发依赖更新 ------ 这就是响应式传播的原理。


三、Pinia 的安装与配置

1️⃣ 安装

csharp 复制代码
npm install pinia
# 或者
yarn add pinia

2️⃣ 创建与挂载

在 main.js 中引入 Pinia 并挂载到 Vue 应用:

javascript 复制代码
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'

const app = createApp(App)
app.use(createPinia())
app.mount('#app')

四、定义第一个 Store

Pinia 推荐使用 defineStore() 来定义一个 store。

javascript 复制代码
// src/stores/counter.js
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', {
  // 1. state:存放共享数据
  state: () => ({
    count: 0,
    name: 'Pinia示例'
  }),

  // 2. getters:相当于计算属性
  getters: {
    doubleCount: (state) => state.count * 2
  },

  // 3. actions:用于定义修改逻辑
  actions: {
    increment() {
      this.count++
    },
    setCount(val) {
      this.count = val
    }
  }
})

五、在组件中使用 Store

在组件中使用时,就像普通变量一样简单。

xml 复制代码
<template>
  <div>
    <h2>{{ counter.name }}</h2>
    <p>当前数量:{{ counter.count }}</p>
    <p>双倍数量:{{ counter.doubleCount }}</p>
    <button @click="counter.increment">增加</button>
  </div>
</template>

<script setup>
import { useCounterStore } from '@/stores/counter'

const counter = useCounterStore()
</script>

💡 响应式自动生效:

当 counter.count 改变时,界面会自动更新,无需手动刷新。


六、解构与响应式陷阱

Pinia store 是响应式对象,如果你用结构赋值要注意保持响应性。

错误写法 ❌:

scss 复制代码
const { count } = useCounterStore()
console.log(count) // 不会响应更新

正确写法 ✅:

javascript 复制代码
import { storeToRefs } from 'pinia'

const store = useCounterStore()
const { count, doubleCount } = storeToRefs(store)

storeToRefs() 会帮你保留响应式引用。


七、模块化管理多个 Store

Pinia 天然支持多 store,无需命名空间:

javascript 复制代码
// user.js
export const useUserStore = defineStore('user', {
  state: () => ({
    name: '张三',
    token: ''
  }),
  actions: {
    login(name) {
      this.name = name
      this.token = 'token123'
    }
  }
})

在组件中可以自由组合使用:

javascript 复制代码
import { useUserStore } from '@/stores/user'
import { useCounterStore } from '@/stores/counter'

const user = useUserStore()
const counter = useCounterStore()

八、持久化存储(localStorage)

Pinia 本身不带持久化功能,但可以通过插件轻松实现:

✅ 手动持久化:

javascript 复制代码
watch(
  () => store.count,
  (newVal) => {
    localStorage.setItem('count', newVal)
  }
)

✅ 使用插件(推荐):

安装:

css 复制代码
npm i pinia-plugin-persistedstate

注册插件:

javascript 复制代码
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'

const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)

开启持久化:

javascript 复制代码
export const useUserStore = defineStore('user', {
  state: () => ({ name: '', token: '' }),
  persist: true
})

九、Pinia + TypeScript 实践(简要示例)

Pinia 的类型推导非常强大。

在 TS 项目中可直接推断出 state 和 getter 的类型。

typescript 复制代码
export const useTodoStore = defineStore('todo', {
  state: () => ({
    list: [] as string[]
  }),
  actions: {
    addTodo(item: string) {
      this.list.push(item)
    }
  }
})

// 自动推导类型
const store = useTodoStore()
store.addTodo('学习 Pinia')

十、Pinia 内部运行机制简述

Pinia 内部核心模块包括:

  1. Store 实例注册

    每个 defineStore() 都注册到全局 pinia._s 容器中。

  2. Reactive 封装

    使用 reactive() 包装 state,配合 Vue 的 effect 机制实现依赖收集。

  3. Getter 包装为 computed

    保证 getter 的懒计算与缓存特性。

  4. Action 包装代理

    使用 Proxy 代理 this 指向当前 store,并自动注入 devtools 日志。

  5. 订阅机制

    Pinia 提供 store.$subscribe(),可以监听 state 变化。


十一、实战示例:Todo 应用

xml 复制代码
<template>
  <div>
    <h2>我的待办事项</h2>
    <input v-model="newTask" @keyup.enter="addTodo" placeholder="输入任务..."/>
    <ul>
      <li v-for="(item, index) in todos.list" :key="index">
        {{ item }}
        <button @click="removeTodo(index)">删除</button>
      </li>
    </ul>
  </div>
</template>

<script setup>
import { ref } from 'vue'
import { useTodoStore } from '@/stores/todo'

const todos = useTodoStore()
const newTask = ref('')

const addTodo = () => {
  if (newTask.value.trim()) {
    todos.addTodo(newTask.value)
    newTask.value = ''
  }
}
const removeTodo = (i) => todos.list.splice(i, 1)
</script>

十二、总结

内容 关键点
状态管理核心 Vue3 的 reactive 与 computed
改进点 无需 mutation、天然模块化
类型支持 友好且强大
响应式机制 Proxy + Effect 依赖追踪
持久化 插件 pinia-plugin-persistedstate
适用场景 中大型 Vue3 项目,全局状态同步

Pinia 的设计哲学是:

"简单到你几乎忘了自己在用状态管理库。"

相关推荐
杰克尼4 小时前
JavaWeb_p165部门管理
java·开发语言·前端
90后的晨仔4 小时前
Vue3 状态管理完全指南:从响应式 API 到 Pinia
前端·vue.js
90后的晨仔4 小时前
Vue 内置组件全解析:提升开发效率的五大神器
前端·vue.js
我胡为喜呀4 小时前
Vue3 中的 watch 和 watchEffect:如何优雅地监听数据变化
前端·javascript·vue.js
我登哥MVP4 小时前
Ajax 详解
java·前端·ajax·javaweb
非凡ghost5 小时前
Typora(跨平台MarkDown编辑器) v1.12.2 中文绿色版
前端·windows·智能手机·编辑器·软件需求
馨谙5 小时前
/dev/null 是什么,有什么用途?
前端·chrome
JamSlade6 小时前
流式响应 sse 系统全流程 react + fastapi为例子
前端·react.js·fastapi
徐同保6 小时前
react useState ts定义类型
前端·react.js·前端框架