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