Vuex 上課筆記:用一個 todos 範例串起 state、mutation、action
上課時做的 Vuex 練習筆記,用一個從 API 載入 todo 清單、存進 store、在元件裡用 mapState 與 mapActions 取用的完整範例,把 state/mutation/action 三個概念串成一個真的能動的流程。完整的 Vuex 概念解說與 Pinia 比較已經整理在另一篇。
W
Vuex 的核心概念——state(狀態)、mutation(提交更改)、action(觸發非同步動作)、getter(取得狀態)——已經整理在《Pinia 與 Vuex:差在哪、怎麼選、怎麼寫》,這篇是上課時做的練習筆記,用一個實際會動的 todos 範例,把那幾個概念串成一個完整流程,留著當個練習紀錄。
Store 設定
export default new Vuex.Store({
state: {
todos: [],
},
mutations: {
setTodos(state, value) {
state.todos = value;
},
},
actions: {
loadTodos(context) {
axios.get('https://jsonplaceholder.typicode.com/todos').then((res) => {
context.commit('setTodos', res.data);
});
},
},
});
流程是:action 負責打 API(非同步動作),拿到資料後呼叫 commit 觸發 mutation,mutation 才是真正改動 state 的地方——這正是 Vuex 規定「非同步的事情放 action、真正改狀態的動作放 mutation」的實際運作樣子。
元件裡使用
<script>
import { mapState, mapMutations, mapActions } from 'vuex';
export default {
computed: {
...mapState(['todos']),
},
mounted() {
this.loadTodos(); // 一開始先呼叫載入
},
methods: {
...mapMutations(['setTodos']),
...mapActions(['loadTodos']),
},
};
</script>
<template>
<div>
<h2>{{ $store.state.todos }}</h2>
<ul>
<li v-for="post in todos" :key="post.id">{{ post.id }}</li>
</ul>
</div>
</template>
{{ $store.state.todos }} 是直接存取 store 的寫法,...mapState(['todos']) 展開後則變成 computed 屬性可以直接用 todos 這個名字取用——兩種寫法效果一樣,mapState/mapMutations/mapActions 這三個輔助函式的用途,就是把 store 的東西映射到元件裡,用起來像一般的 computed/methods。
搭配路由參數做篩選
<script>
import { mapState, mapMutations, mapActions } from 'vuex';
export default {
computed: {
...mapState(['todos']),
todoId() {
return this.todos.filter((todo) => todo.userId == this.$route.params.num);
},
},
mounted() {
this.loadTodos();
},
methods: {
...mapMutations(['setTodos']),
...mapActions(['loadTodos']),
},
};
</script>
<template>
<div>
<p>you enter by id todos {{ todoId }}</p>
</div>
</template>
todoId 是一個 computed,依賴 todos(store 的狀態)跟 $route.params.num(路由參數),任何一個變動都會重新計算——這就是為什麼「取得符合某個條件的資料」通常會寫成 computed 而不是在 mounted 裡算一次就結束,資料來源一變,畫面才會自動跟著更新。