Skip to main content

Vuex vs Pinia 差異比較

比較 Vuex 和 Pinia 的核心差異,包含 API 設計、TypeScript 支援、模組化方式等,並提供遷移指南。


1. 面試回答主軸

  1. 核心差異:Vuex 需要 mutations,Pinia 不需要;Pinia 有更好的 TypeScript 支援;模組化方式不同。
  2. 選擇建議:Vue 3 新專案推薦 Pinia,Vue 2 專案使用 Vuex。
  3. 遷移考量:從 Vuex 遷移到 Pinia 的步驟與注意事項。

2. 核心差異總覽

特性VuexPinia
Vue 版本Vue 2Vue 3
API 複雜度較複雜(需要 mutations)更簡潔(不需要 mutations)
TypeScript 支援需要額外配置原生完整支援
模組化嵌套模組扁平化,每個 store 獨立
體積較大更小(約 1KB)
開發體驗良好更好(HMR、Devtools)

3. API 差異比較

3.1 Mutations vs Actions

Vuex:需要 mutations 來同步修改 state

// Vuex
export default createStore({
state: { count: 0 },
mutations: {
INCREMENT(state) {
state.count++;
},
},
actions: {
increment({ commit }) {
commit('INCREMENT');
},
},
});

Pinia:不需要 mutations,直接在 actions 中修改 state

// Pinia
export const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 }),
actions: {
increment() {
this.count++; // 直接修改
},
},
});

關鍵差異

  • Vuex:必須透過 mutations 同步修改 state,actions 透過 commit 調用 mutations
  • Pinia:不需要 mutationsactions 可以直接修改 state(同步或非同步都可以)

3.2 State 定義

Vuexstate 可以是物件或函數

state: {
count: 0,
}

Piniastate 必須是函數,避免多實例共享狀態

state: () => ({
count: 0,
})

3.3 Getters

Vuex:getters 接收 (state, getters) 作為參數

getters: {
doubleCount: (state) => state.count * 2,
doubleCountPlusOne: (state, getters) => getters.doubleCount + 1,
}

Pinia:getters 可以使用 this 訪問其他 getters

getters: {
doubleCount: (state) => state.count * 2,
doubleCountPlusOne(): number {
return this.doubleCount + 1;
},
}

3.4 在組件中使用

Vuex:使用 mapStatemapGettersmapActions 輔助函數

computed: {
...mapState(['count']),
...mapGetters(['doubleCount']),
},
methods: {
...mapActions(['increment']),
}

Pinia:直接使用 store 實例,使用 storeToRefs 保持響應性

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

4. 模組化差異

4.1 Vuex Modules(嵌套模組)

Vuex:使用嵌套模組,需要 namespaced: true

// stores/user.js
export default {
namespaced: true,
state: { name: 'John' },
mutations: {
SET_NAME(state, name) {
state.name = name;
},
},
};

// 在組件中使用
this.$store.dispatch('user/SET_NAME', 'Jane'); // 需要命名空間前綴

4.2 Pinia Stores(扁平化)

Pinia:每個 store 都是獨立的,無需嵌套

// stores/user.ts
export const useUserStore = defineStore('user', {
state: () => ({ name: 'John' }),
actions: {
setName(name: string) {
this.name = name;
},
},
});

// 在組件中使用
const userStore = useUserStore();
userStore.setName('Jane'); // 直接調用,無需命名空間

關鍵差異

  • Vuex:需要嵌套模組,使用 namespaced: true,調用時需要命名空間前綴
  • Pinia:每個 store 獨立,無需命名空間,直接調用

5. TypeScript 支援差異

5.1 Vuex TypeScript 支援

Vuex:需要額外配置型別

// stores/types.ts
export interface State {
count: number;
user: { name: string; age: number };
}

// stores/index.ts
import { createStore, Store } from 'vuex';
import { State } from './types';

export default createStore<State>({
state: { count: 0, user: { name: 'John', age: 30 } },
});

// 在組件中使用
const store = useStore<State>();
// 需要手動定義型別,沒有完整的型別推斷

5.2 Pinia TypeScript 支援

Pinia:原生完整支援,自動型別推斷

// stores/counter.ts
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
user: { name: 'John', age: 30 },
}),
getters: {
doubleCount: (state) => state.count * 2, // 自動推斷型別
},
actions: {
increment() {
this.count++; // 完整的型別推斷和自動完成
},
},
});

// 在組件中使用
const store = useCounterStore();
store.count; // 完整的型別推斷
store.doubleCount; // 完整的型別推斷
store.increment(); // 完整的型別推斷

關鍵差異

  • Vuex:需要手動定義型別,型別推斷不完整
  • Pinia:原生完整支援,自動型別推斷,開發體驗更好

6. 遷移指南

6.1 基本遷移步驟

  1. 安裝 Pinia
npm install pinia
  1. 替換 Vuex Store
// 舊的 Vuex
import { createStore } from 'vuex';
export default createStore({ ... });

// 新的 Pinia
import { createPinia } from 'pinia';
const pinia = createPinia();
app.use(pinia);
  1. 轉換 Store 定義
// Vuex
export default createStore({
state: { count: 0 },
mutations: {
INCREMENT(state) {
state.count++;
},
},
actions: {
increment({ commit }) {
commit('INCREMENT');
},
},
});

// Pinia
export const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 }),
actions: {
increment() {
this.count++;
},
},
});
  1. 更新組件使用方式
// Vuex
import { mapState, mapActions } from 'vuex';
computed: { ...mapState(['count']) },
methods: { ...mapActions(['increment']) },

// Pinia
import { storeToRefs } from 'pinia';
const store = useCounterStore();
const { count } = storeToRefs(store);
const { increment } = store;

6.2 常見遷移問題

問題 1:如何處理 Vuex modules?

// Vuex modules
modules: {
user: userModule,
product: productModule,
}

// Pinia:每個模組變成獨立的 store
// stores/user.ts
export const useUserStore = defineStore('user', { ... });

// stores/product.ts
export const useProductStore = defineStore('product', { ... });

問題 2:如何處理命名空間?

// Vuex:需要命名空間前綴
this.$store.dispatch('user/SET_NAME', 'John');

// Pinia:直接調用,無需命名空間
const userStore = useUserStore();
userStore.setName('John');

7. 為什麼 Pinia 不需要 mutations?

原因

  1. Vue 3 的響應式系統

    • Vue 3 使用 Proxy,可以直接追蹤物件的修改
    • 不需要像 Vue 2 那樣透過 mutations 來追蹤狀態變化
  2. 簡化 API

    • 移除 mutations 可以簡化 API,減少樣板程式碼
    • Actions 可以直接修改 state,無論是同步還是非同步操作
  3. 開發體驗

    • 減少一層抽象,開發者更容易理解和使用
    • 不需要記住 commitdispatch 的區別

範例

// Vuex:需要 mutations
mutations: { SET_COUNT(state, count) { state.count = count; } },
actions: { setCount({ commit }, count) { commit('SET_COUNT', count); } },

// Pinia:直接修改
actions: { setCount(count) { this.count = count; } },

8. 如何選擇使用 Vuex 還是 Pinia?

選擇建議

  1. 新專案

    • Vue 3 專案:推薦使用 Pinia
    • Vue 2 專案:使用 Vuex
  2. 現有專案

    • Vue 2 + Vuex:可以繼續使用 Vuex,或考慮升級到 Vue 3 + Pinia
    • Vue 3 + Vuex:可以考慮遷移到 Pinia(但非必須)
  3. 專案需求

    • 需要完整 TypeScript 支援:選擇 Pinia
    • 需要更簡潔的 API:選擇 Pinia
    • 團隊熟悉 Vuex:可以繼續使用 Vuex

總結

  • Vue 3 新專案:強烈推薦 Pinia
  • Vue 2 專案:使用 Vuex
  • 現有 Vue 3 + Vuex 專案:可以考慮遷移,但非必須

9. 面試重點整理

9.1 核心差異

可以這樣回答:

Vuex 和 Pinia 都是 Vue 的狀態管理工具,主要差異包括:1) API 複雜度:Vuex 需要 mutations 來同步修改 state,Pinia 不需要 mutations,actions 可以直接修改 state;2) TypeScript 支援:Vuex 需要額外配置,型別推斷不完整,Pinia 原生完整支援,自動型別推斷;3) 模組化:Vuex 使用嵌套模組,需要 namespaced,Pinia 每個 store 獨立,無需命名空間;4) 開發體驗:Pinia 體積更小、支援 HMR、更好的 Devtools 支援;5) Vue 版本:Vuex 主要用於 Vue 2,Pinia 是 Vue 3 的官方推薦。對於 Vue 3 新專案,我推薦使用 Pinia。

關鍵點:

  • ✅ API 複雜度差異
  • ✅ TypeScript 支援差異
  • ✅ 模組化方式差異
  • ✅ 選擇建議

9.2 為什麼 Pinia 不需要 mutations?

可以這樣回答:

Pinia 不需要 mutations 主要有三個原因:1) Vue 3 使用 Proxy 作為響應式系統,可以直接追蹤物件的修改,不需要像 Vue 2 那樣透過 mutations 來追蹤狀態變化;2) 簡化 API,移除 mutations 可以減少樣板程式碼,actions 可以直接修改 state,無論是同步還是非同步操作;3) 提升開發體驗,減少一層抽象,開發者更容易理解和使用,不需要記住 commit 和 dispatch 的區別。

關鍵點:

  • ✅ Vue 3 響應式系統
  • ✅ API 簡化
  • ✅ 開發體驗提升

10. 面試總結

可以這樣回答:

Vuex 和 Pinia 的主要差異在於 API 設計、TypeScript 支援和模組化方式。Vuex 需要 mutations,Pinia 不需要;Pinia 有更好的 TypeScript 支援;Vuex 使用嵌套模組,Pinia 使用扁平化設計。對於 Vue 3 新專案,我推薦使用 Pinia,因為它提供更好的開發體驗和更簡潔的 API。如果專案需要從 Vuex 遷移到 Pinia,主要步驟是移除 mutations,將 modules 轉換為獨立的 stores,並更新組件使用方式。

關鍵點:

  • ✅ 核心差異總結
  • ✅ 選擇建議
  • ✅ 遷移指南
  • ✅ 實際專案經驗

Reference