Skip to content

Pinia 状态管理

Pinia 是 Vue 3 官方推荐的状态管理库,比 Vuex 更简洁,完整支持 TypeScript,且支持 Composition API 风格。

为什么选 Pinia

特性Vuex 4Pinia
TypeScript 支持一般完整
Composition API不支持原生支持
mutations必须不需要
模块嵌套复杂扁平 Store
DevTools支持支持
体积~10KB~1KB

安装与配置

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

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

定义 Store

Option Store 风格

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

export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0,
    name: 'Eduardo'
  }),

  getters: {
    doubled: (state) => state.count * 2,
    // 使用其他 getter
    doubledPlusOne(): number {
      return this.doubled + 1
    }
  },

  actions: {
    increment() {
      this.count++
    },
    async fetchUser(userId: number) {
      const user = await api.getUser(userId)
      this.name = user.name
    }
  }
})

Setup Store 风格(推荐)

ts
// stores/user.ts
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'

export const useUserStore = defineStore('user', () => {
  // state
  const user = ref<User | null>(null)
  const loading = ref(false)
  const error = ref<string | null>(null)

  // getters
  const isLoggedIn = computed(() => user.value !== null)
  const displayName = computed(() =>
    user.value ? `${user.value.firstName} ${user.value.lastName}` : '游客'
  )

  // actions
  async function login(credentials: LoginCredentials) {
    loading.value = true
    error.value = null
    try {
      user.value = await authApi.login(credentials)
    } catch (e) {
      error.value = (e as Error).message
    } finally {
      loading.value = false
    }
  }

  function logout() {
    user.value = null
    authApi.logout()
  }

  return { user, loading, error, isLoggedIn, displayName, login, logout }
})

在组件中使用

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

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

// ✅ storeToRefs:解构保持响应性(state 和 getters)
const { user, loading, isLoggedIn, displayName } = storeToRefs(userStore)

// actions 直接解构(不需要 storeToRefs)
const { login, logout } = userStore

// 直接修改 state(Pinia 允许)
function updateName() {
  userStore.user!.name = 'New Name'
}

// $patch:批量修改
function batchUpdate() {
  userStore.$patch({
    loading: false,
    error: null
  })
  // 或函数形式(适合复杂修改)
  userStore.$patch(state => {
    state.loading = false
    state.error = null
  })
}

// $reset:重置到初始状态(Option Store)
function reset() {
  counterStore.$reset()
}
</script>

<template>
  <div>
    <p v-if="loading">加载中...</p>
    <p v-else-if="isLoggedIn">欢迎,{{ displayName }}</p>
    <button @click="logout">退出</button>
  </div>
</template>

Store 间通信

ts
// stores/cart.ts
import { defineStore } from 'pinia'
import { useUserStore } from './user'

export const useCartStore = defineStore('cart', () => {
  const items = ref<CartItem[]>([])

  // 在 action 中使用其他 store
  async function checkout() {
    const userStore = useUserStore()  // 在函数内部调用,避免循环依赖

    if (!userStore.isLoggedIn) {
      throw new Error('请先登录')
    }

    await orderApi.create({
      userId: userStore.user!.id,
      items: items.value
    })

    items.value = []
  }

  return { items, checkout }
})

持久化插件

ts
// pinia-plugin-persistedstate
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'

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

// 在 store 中启用
export const useAuthStore = defineStore('auth', () => {
  const token = ref<string | null>(null)
  return { token }
}, {
  persist: {
    key: 'auth',
    storage: localStorage,
    paths: ['token']  // 只持久化 token
  }
})

自定义插件

ts
// 日志插件
import type { PiniaPluginContext } from 'pinia'

function loggerPlugin({ store }: PiniaPluginContext) {
  store.$subscribe((mutation, state) => {
    console.log(`[${store.$id}] ${mutation.type}:`, mutation)
  })

  store.$onAction(({ name, args, after, onError }) => {
    console.log(`[${store.$id}] action: ${name}`, args)
    after(result => console.log(`[${store.$id}] result:`, result))
    onError(error => console.error(`[${store.$id}] error:`, error))
  })
}

pinia.use(loggerPlugin)

总结

  • Setup Store 风格更灵活,与 Composition API 完全一致
  • storeToRefs 解构 state/getters 保持响应性,actions 直接解构
  • Pinia 允许直接修改 state,无需 mutations
  • Store 间通信在 action 内部调用其他 store(避免循环依赖)
  • 配合 pinia-plugin-persistedstate 轻松实现状态持久化

系统学习 Web 前端生态,深入底层架构