Vuex-使用筆記
簡介
- 主要用於管理 Vue 專案中共享的狀態
- 提供了一個集中式存儲來管理所有元件的狀態
- 來自不同元件的行為修改同一個狀態時,可以同時更新用到這狀態的所有地方
核心概念
state
- 如同 data,是存放資料的地方
action
- 如同 methods,用來處理非同步事件及取得遠端資料,但不負責處理資料內容的改變
getter
- 如同 computed,在畫面渲染前先對資料進行運算及過濾等
mutation
- 實際用於改變資料的內容
示意圖

如何使用 ( Vue3 + Vuex4 )
- 使用 Vue CLI 創建專案,並選擇配置 Vuex
- 在
sotre
資料夾中調整index.js
檔案
import { createStore } from 'vuex'
export default createStore({
state() {
return {
count: 1
}
},
getters: {},
mutations: {
increment(state, payload) {
state.count += payload
}
},
actions: {},
modules: {}
})
- 在
main.js
檔案中引入 store
// main.js
import { createApp } from 'vue'
import { store } from './store'
import App from './App.vue'
const app = createApp(App)
app.use(store)
app.mount('#app')
- 在元件內 import
useStore
取得 store 實例
- Vuex 的
state
通常會放到computed
內
<!-- ./components/CountItem.vue -->
<template>
{{ count }}
<div>
<button type="button" @click="decreasement(6)">minus</button>
<button type="button" @click="increase(6)">plus</button>
</div>
</template>
<script>
import { useStore } from 'vuex'
import { computed } from 'vue'
export default {
name: 'CountItem',
setup() {
const store = useStore()
const count = computed(() => store.state.count)
const increase = (num) => {
store.commit('increment', num)
}
const decreasement = (num) => {
store.commit('decreasement', num)
}
return {
count,
increase,
decreasement
}
}
}
</script>
運作流程
在同步操作和簡單邏輯時
mutations
只能處理同步邏輯
- 通過
store.state
來獲取狀態對象,並通過store.commit
方法觸發狀態變更 - 可以跳過 actions,直接使用
mutations
改變狀態
在進行非同步操作時
非同步邏輯,必須使用
actions
- 元件透過
dispatch
方法來觸發actions
事件 actions
執行 AJAX,取得遠端的資料- 接著
actions
使用commit
方式去呼叫mutations
- 透過
mutations
去改變資料狀態(state
) - 最後把資料狀態回應給元件去渲染畫面
// ./store/index.js
import { createStore } from 'vuex'
export default createStore({
state() {
return {
todo: null
}
},
mutations: {
SET_DATA(state, newData) {
state.todo = newData // 更新狀態
}
},
actions: {
async fetchData({ commit }) {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/todos/1')
const data = await response.json()
commit('SET_DATA', data) // 使用commit呼叫mutation
} catch (error) {
console.error('Fetch data error:', error)
}
}
}
})
<!-- TodoList.vue -->
<template>
<div v-if="todo">
{{ todo }}
</div>
<button @click="getTodo">取得Todo</button>
</template>
<script>
import { computed } from 'vue'
import { useStore } from 'vuex'
export default {
setup() {
const store = useStore()
const todo = computed(() => store.state.todo)
const getTodo = async () => {
await store.dispatch('fetchData')
}
return {
todo,
getTodo
}
}
}
</script>
modules 模組化
- 當專案規模變大時,所有的狀態都放在
index.js
- 可以利用 modules 依照功能區分,以便維護管理
操作步驟:
- 在 store 資料夾,新增
modules
- 新增
user.js
存放使用者相關的資料 - 設定
namespaced: true
,替模組加上命名,會自動根據路徑調整命名
// user.js
export const user = {
namespaced: true,
state: () => ({
name: 'Ben'
}),
mutations: {
SET_NAME(state, name) {
state.name = name
}
},
actions: {
updateName({ commit }, name) {
commit('SET_NAME', name)
}
},
getters: {
userName: (state) => state.name
}
}
- 在 Vue 元件內,可以這樣使用
<!-- User.vue -->
<template>
<div>
<p>User: {{ userName }}</p>
<button type="button" @click="changeName('Pan')">change</button>
</div>
</template>
<script>
import { useStore } from 'vuex'
import { computed } from 'vue'
export default {
name: 'App',
setup() {
const store = useStore()
const userName = computed(() => store.getters['user/userName'])
const changeName = (name) => {
store.dispatch('user/updateName', name)
}
return {
userName,
changeName
}
}
}
</script>
map 輔助函式(Options API)
- 在 Options API 中,隨著專案規模擴大,例如要把 Vuex 當中的每個 state,都使用
computed
宣告較麻煩 - 提供 map 輔助函式,提供更簡潔的寫法
- mapState
- mapGetters
- mapMutations
- mapActions
- 以下使用 Vue2 的 Options API 寫法
// ./store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count: 0,
isLoggedIn: false,
userName: 'Guest'
},
getters: {
doubleCount: (state) => state.count * 2
},
mutations: {
increment(state) {
state.count++
}
},
actions: {
incrementAsync({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
}
})
<template>
<div>
<p>Count: {{ count }}</p>
<p>User Name: {{ userName }}</p>
<p>Logged In: {{ isLoggedIn }}</p>
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
computed: {
// 使用 mapState 的陣列形式
...mapState([
'count', // 映射 this.count 為 store.state.count
'isLoggedIn', // 映射 this.isLoggedIn 為 store.state.isLoggedIn
'userName' // 映射 this.userName 為 store.state.userName
])
// 其他computed屬性
}
// 其他methods
}
</script>
檔案結構
├── index.html
├── main.js
├── api
│ └── ... # 抽取出API請求
├── components
│ ├── App.vue
│ └── ...
└── store
├── index.js # 彙整所有的store,並導出
├── actions.js # 當專案規模很大時,可以把actions單獨拆出來
├── mutations.js # 當專案規模很大時,可以把mutations單獨拆出來
└── modules
├── cart.js # 購物車模塊
└── products.js # 產品模塊
參考資料
# Vue # Vuex