最近在接手vue項目的需求,因為之前一直在做react的需求,日常的vue練習也少了很多,導致現在接手vue項目,很多關于vue的知識點基本上忘得干干凈凈了。但是好在有基礎,重新學也會很快掌握。分享這個過程中的一些復習內容。
本篇分享【狀態管理】相關的內容。
一、狀態管理概述
狀態管理用于解決 Vue 應用中組件間的數據共享問題,尤其適合以下場景:
- 多個組件需要共享同一數據
- 不同組件需要修改同一份數據
- 跨層級組件間的通信
Vuex 作為 Vue 官方推薦的狀態管理庫已有多年歷史,而 Pinia 則是 Vuex 團隊在 Vue 3 時代推出的新方案,目前已成為 Vue 3 項目的官方推薦。
二、Vuex 4.x 的使用方法
Vuex 4 專為 Vue 3 設計,保留了 Vuex 的核心概念,但做了適配 Composition API 的改進。
1. 安裝與配置
npm install vuex@4
創建 store/index.js:
import { createStore } from 'vuex'export default createStore({state() {return {count: 0,message: 'Hello from Vuex'}},mutations: {increment(state) {state.count++},updateMessage(state, newMessage) {state.message = newMessage}},actions: {async updateMessageAsync(context, newMessage) {// 模擬API請求await new Promise(resolve => setTimeout(resolve, 500))context.commit('updateMessage', newMessage)}},getters: {doubleCount(state) {return state.count * 2}}
})
在 main.js 中引入:
import { createApp } from 'vue'
import App from './App.vue'
import store from './store'createApp(App).use(store).mount('#app')
2. 核心概念
- State:存儲應用狀態的數據源
- Mutations:唯一可以修改 state 的同步函數
- Actions:處理異步操作,通過 commit 調用 mutations
- Getters:從 state 派生出的計算屬性
3. 組件中使用
src/component/VuexPage1.vue
<template><div class="vuex-page1"><h2>Vuex Page 1</h2><p>Count from store: {{ $store.state.count }}</p><p>Double Count: {{ $store.getters.doubleCount }}</p><p>Message: {{ $store.state.message }}</p><button @click="$store.commit('decrement')">Decrement</button><button @click="$store.dispatch('increment')">Increment (via action)</button><button @click="updateMessage">Change Message</button></div>
</template><script setup>
import { useStore } from 'vuex'// 獲取store實例
const store = useStore()// 組件方法
const updateMessage = () => {store.commit('updateMessage', 'Updated from Vuex Page 1')
}
</script><style scoped>
.vuex-page1 {padding: 20px;border: 1px solid #42b983;margin: 10px;background-color: #f0fdf4;
}button {margin: 0 5px;padding: 5px 10px;cursor: pointer;
}
</style>
src/component/VuexPage2.vue
<template><div class="vuex-page2"><h2>Vuex Page 2</h2><p>Count from store: {{ $store.state.count }}</p><p>Double Count: {{ $store.getters.doubleCount }}</p><p>Message: {{ $store.state.message }}</p><button @click="$store.commit('increment')">Increment</button><button @click="$store.commit('reset')">Reset Count</button><button @click="updateMessageAsync">Change Message (Async)</button></div>
</template><script setup>
import { useStore } from 'vuex'// 獲取store實例
const store = useStore()// 組件方法
const updateMessageAsync = () => {store.dispatch('updateMessageAsync', 'Updated from Vuex Page 2 (Async)')
}
</script><style scoped>
.vuex-page2 {padding: 20px;border: 1px solid #3498db;margin: 10px;background-color: #f0f7ff;
}button {margin: 0 5px;padding: 5px 10px;cursor: pointer;
}
</style>
App.vue
<template><div id="app"><h1>Vuex Store Test</h1><VuexPage1 /><VuexPage2 /></div>
</template><script setup>
import VuexPage1 from './components/VuexPage1.vue'
import VuexPage2 from './components/VuexPage2.vue'
</script><style>
#app {font-family: Avenir, Helvetica, Arial, sans-serif;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;text-align: center;color: #2c3e50;margin-top: 60px;
}
</style>
?4.效果展示
三、Pinia 的使用方法
Pinia 是 Vuex 的繼任者,簡化了狀態管理的寫法,天然支持 TypeScript,更符合 Vue 3 的 Composition API 風格。
1. 安裝與配置
npm install pinia
創建 store/index.js:
import { createPinia } from 'pinia'
export default createPinia()
創建 store/counterStore.js:
import { defineStore } from 'pinia'export const useCounterStore = defineStore('counter', {state: () => ({count: 0,message: 'Hello from Pinia'}),actions: {increment() {this.count++},async updateMessageAsync(newMessage) {await new Promise(resolve => setTimeout(resolve, 500))this.message = newMessage}},getters: {doubleCount() {return this.count * 2}}
})
在 main.js 中引入:
import { createApp } from 'vue'
import App from './App.vue'
import store from './store'createApp(App).use(store).mount('#app')
2. 核心概念
- Store:每個 store 都是一個獨立的模塊
- State:存儲狀態的數據源
- Actions:可以包含同步和異步操作,直接修改 state
- Getters:計算屬性,基于 state 派生
3. 組件中使用
src/component/Page1.vue
<template><div class="page1"><h2>Page 1</h2><p>Count from store: {{ counterStore.count }}</p><p>Message: {{ counterStore.message }}</p><button @click="counterStore.increment">Increment</button><button @click="counterStore.decrement">Decrement</button><button @click="counterStore.changeMultiplier(5)">changeMultiplier</button><button @click="beMultiplier">beMultiplier</button><button @click="changeMessage">Change Message</button></div>
</template><script setup>
import { useCounterStore } from '../store/counterStore'// 獲取store實例
const counterStore = useCounterStore()// 組件方法
const changeMessage = () => {counterStore.updateMessage('Updated from Page 1')
}
const beMultiplier=()=>{counterStore.beMultipliedBy(4)
}
</script><style scoped>
.page1 {padding: 20px;border: 1px solid #ccc;margin: 10px;
}button {margin: 0 5px;padding: 5px 10px;
}
</style>
src/component/Page2.vue
<template><div class="page2"><h2>Page 2</h2><p>Count from store: {{ counterStore.count }}</p><p>Message: {{ counterStore.message }}</p><button @click="counterStore.increment">Increment</button><button @click="counterStore.reset">Reset Count</button><button @click="changeMessage">Change Message</button></div>
</template><script setup>
import { useCounterStore } from '../store/counterStore'// 獲取store實例
const counterStore = useCounterStore()// 組件方法
const changeMessage = () => {counterStore.updateMessage('Updated from Page 2')
}
</script><style scoped>
.page2 {padding: 20px;border: 1px solid #666;margin: 10px;background-color: #f5f5f5;
}button {margin: 0 5px;padding: 5px 10px;
}
</style>
App.vue
<template><div id="app"><h1>Pinia Store Test</h1><Page1 /><Page2 /></div>
</template><script setup>
import Page1 from './components/Page1.vue'
import Page2 from './components/Page2.vue'
</script><style>
#app {font-family: Avenir, Helvetica, Arial, sans-serif;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;text-align: center;color: #2c3e50;margin-top: 60px;
}
</style>
4.?效果展示
?
四、Pinia 與 Vuex 的核心區別
特性 | Vuex 4 | Pinia |
---|---|---|
模塊化 | 需要通過 modules 創建模塊 | 每個 store 都是獨立模塊,天然支持模塊化 |
狀態修改 | 必須通過 mutations | 直接在 actions 中修改,或直接修改 state |
異步操作 | 需要在 actions 中處理 | 可以在 actions 中直接處理 |
TypeScript 支持 | 有限,需要額外類型定義 | 原生支持,類型推斷更友好 |
代碼簡潔性 | 較繁瑣,需要 commit/dispatch 區分 | 更簡潔,直接調用方法 |
開發者工具支持 | 支持 | 支持,且有更好的時間線追蹤 |
插件系統 | 豐富 | 兼容 Vuex 的插件,且有新的插件系統 |
五、如何選擇
新項目:優先選擇 Pinia,它是 Vue 官方推薦的最新方案,API 更簡潔,TypeScript 支持更好。
已有 Vuex 項目:
- 若項目穩定運行,無需急于遷移
- 若進行重大重構,可考慮遷移到 Pinia
- Vuex 4 仍會維護,但不會有新功能
團隊因素:如果團隊已熟悉 Vuex,且項目復雜度不高,繼續使用 Vuex 也是合理選擇。
六、總結
Pinia 和 Vuex 都是優秀的 Vue 狀態管理方案,它們解決的核心問題相同,但實現方式有所不同。
Vuex 作為成熟的狀態管理庫,有著完善的生態和社區支持,適合需要嚴格規范狀態修改流程的大型項目。而 Pinia 則代表了未來的發展方向,它簡化了狀態管理的寫法,提供了更好的開發體驗和 TypeScript 支持。