Pinia vs Vuex 对比 #

概述 #

Pinia 和 Vuex 都是 Vue.js 的状态管理库。Pinia 作为 Vuex 的继任者,在设计上更加简洁和现代化。本节将详细对比两者的差异。

核心概念对比 #

结构对比 #

text
Vuex 结构
├── State ──────── 状态数据
├── Getters ────── 计算属性
├── Mutations ──── 同步修改(必须)
└── Actions ────── 异步操作

Pinia 结构
├── State ──────── 状态数据
├── Getters ────── 计算属性
└── Actions ────── 同步/异步操作(无 Mutations)

代码量对比 #

Vuex 示例 #

ts
// store/modules/counter.ts
import { Module } from 'vuex'
import { RootState } from '../index'

interface CounterState {
  count: number
}

const counterModule: Module<CounterState, RootState> = {
  namespaced: true,
  
  state: () => ({
    count: 0
  }),
  
  getters: {
    doubleCount: (state) => state.count * 2
  },
  
  mutations: {
    INCREMENT(state) {
      state.count++
    },
    
    INCREMENT_BY(state, payload: number) {
      state.count += payload
    },
    
    RESET(state) {
      state.count = 0
    }
  },
  
  actions: {
    increment({ commit }) {
      commit('INCREMENT')
    },
    
    incrementBy({ commit }, payload: number) {
      commit('INCREMENT_BY', payload)
    },
    
    reset({ commit }) {
      commit('RESET')
    },
    
    async fetchInitialValue({ commit }) {
      const response = await fetch('/api/initial-value')
      const value = await response.json()
      commit('INCREMENT_BY', value)
    }
  }
}

export default counterModule

Pinia 示例 #

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

export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0
  }),
  
  getters: {
    doubleCount: (state) => state.count * 2
  },
  
  actions: {
    increment() {
      this.count++
    },
    
    incrementBy(payload: number) {
      this.count += payload
    },
    
    reset() {
      this.count = 0
    },
    
    async fetchInitialValue() {
      const response = await fetch('/api/initial-value')
      const value = await response.json()
      this.count += value
    }
  }
})

详细对比表 #

特性 Pinia Vuex
核心概念 State, Getters, Actions State, Getters, Mutations, Actions
Mutations 必须通过 Mutations 修改状态
模块化 每个 Store 独立 需要手动配置 modules
命名空间 自动(Store ID) 需要手动开启 namespaced
TypeScript 原生支持,自动推断 需要额外配置
代码量
学习曲线
DevTools 完整支持 完整支持
包体积 ~1KB ~3KB
Vue 版本 Vue 2 & 3 Vue 2 & 3
Composition API 完美支持 需要额外 API
热更新 简单 复杂

主要差异详解 #

1. Mutations 的移除 #

Vuex:必须通过 Mutations 修改状态

ts
// Vuex
mutations: {
  SET_USER(state, user) {
    state.user = user
  }
},
actions: {
  async login({ commit }, credentials) {
    const user = await api.login(credentials)
    commit('SET_USER', user)
  }
}

Pinia:直接在 Actions 中修改

ts
// Pinia
actions: {
  async login(credentials) {
    this.user = await api.login(credentials)
  }
}

2. 模块化 #

Vuex:需要手动配置嵌套模块

ts
// Vuex
const store = createStore({
  modules: {
    user: userModule,
    cart: cartModule,
    product: productModule
  }
})

// 访问
this.$store.state.user.name
this.$store.dispatch('user/login')

Pinia:每个 Store 独立

ts
// Pinia
const userStore = useUserStore()
const cartStore = useCartStore()
const productStore = useProductStore()

// 访问
userStore.name
userStore.login()

3. TypeScript 支持 #

Vuex:需要大量类型定义

ts
// Vuex
import { createStore, Store } from 'vuex'

interface State {
  count: number
}

declare module '@vue/runtime-core' {
  interface ComponentCustomProperties {
    $store: Store<State>
  }
}

const store = createStore<State>({
  state: {
    count: 0
  }
})

Pinia:开箱即用的类型支持

ts
// Pinia
export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0  // 自动推断为 number
  })
})

4. Composition API 支持 #

Vuex:使用 useStore

ts
import { useStore } from 'vuex'

export default {
  setup() {
    const store = useStore()
    
    const count = computed(() => store.state.count)
    const doubleCount = computed(() => store.getters.doubleCount)
    
    function increment() {
      store.dispatch('increment')
    }
    
    return { count, doubleCount, increment }
  }
}

Pinia:直接使用 Store

ts
import { useCounterStore } from '@/stores/counter'
import { storeToRefs } from 'pinia'

export default {
  setup() {
    const store = useCounterStore()
    
    const { count, doubleCount } = storeToRefs(store)
    const { increment } = store
    
    return { count, doubleCount, increment }
  }
}

5. Setup Store 风格 #

Pinia 独有:支持 Composition API 风格

ts
export const useCounterStore = defineStore('counter', () => {
  const count = ref(0)
  const doubleCount = computed(() => count.value * 2)
  
  function increment() {
    count.value++
  }
  
  return { count, doubleCount, increment }
})

功能对比 #

状态持久化 #

Vuex:需要手动实现或使用第三方库

ts
// Vuex 持久化
const persistedState = localStorage.getItem('vuex')
const store = createStore({
  state: persistedState ? JSON.parse(persistedState) : initialState,
  plugins: [
    store => {
      store.subscribe((mutation, state) => {
        localStorage.setItem('vuex', JSON.stringify(state))
      })
    }
  ]
})

Pinia:使用 pinia-plugin-persistedstate

ts
// Pinia 持久化
export const useUserStore = defineStore('user', {
  state: () => ({ name: '', token: '' }),
  persist: true
})

热更新 #

Vuex:复杂的 HMR 配置

ts
// Vuex HMR
if (import.meta.hot) {
  import.meta.hot.accept(['./store/modules/user'], (newModules) => {
    const newUserModule = newModules[0].default
    store.hotUpdate({
      modules: {
        user: newUserModule
      }
    })
  })
}

Pinia:简单的 HMR 配置

ts
// Pinia HMR
if (import.meta.hot) {
  import.meta.hot.accept(acceptHMRUpdate(useUserStore, import.meta.hot))
}

插件系统 #

Vuex:插件接收 store

ts
// Vuex 插件
const myPlugin = (store) => {
  store.subscribe((mutation, state) => {
    console.log(mutation.type)
  })
}

Pinia:插件接收 context

ts
// Pinia 插件
const myPlugin = ({ store }) => {
  store.$subscribe((mutation, state) => {
    console.log(mutation.type)
  })
}

迁移建议 #

何时选择 Pinia #

  • 新项目
  • 使用 Vue 3
  • 需要 TypeScript 支持
  • 希望更简洁的代码
  • 使用 Composition API

何时继续使用 Vuex #

  • 现有大型项目
  • 团队熟悉 Vuex
  • 需要严格的状态修改追踪
  • 使用 Vue 2 且不需要迁移

迁移成本 #

低成本场景 #

  • 小型项目
  • 简单的状态管理
  • 良好的 TypeScript 支持

高成本场景 #

  • 大型项目
  • 复杂的模块依赖
  • 大量使用 Vuex 特有功能

总结 #

方面 Pinia Vuex
推荐程度 ⭐⭐⭐⭐⭐ ⭐⭐⭐
新项目 推荐 不推荐
学习难度
代码简洁度
TypeScript 优秀 一般
维护成本

Pinia 作为 Vue 官方推荐的状态管理库,在各方面都优于 Vuex。对于新项目,强烈推荐使用 Pinia。

下一步 #

现在你已经了解了 Pinia 和 Vuex 的差异,接下来让我们学习如何从 Vuex 迁移到 Pinia。

最后更新:2026-03-28