在以dispatch呼叫actiion後,進而發送API請求,詳細的流程,要進一步查看Vuex的模組物件(module object),可以查看src/store/employee.module.js
,參考如下。
// src/store/employee.module.js
import Vue from "vue";
import { EmployeeService } from "@/common/api.service";
import { FETCH_EMPLOYEES } from "@/store/actions.type";
import {
SET_EMPLOYEES,
SET_EMPLOYEE_COUNT,
SET_NEXT_URL,
SET_PREV_URL,
SET_CURRENT_PAGE,
SET_LOADING,
} from "@/store/mutations.type";
// 呼叫actions
export const actions = {
async [FETCH_EMPLOYEES](context, params) {
context.commit(SET_LOADING, true); // 可用來控制畫面的載入動畫(開始)
try {
const { data } = await EmployeeService.query(params); // 呼叫API拿資料
context.commit(SET_EMPLOYEES, data.results); // 儲存資料列表
context.commit(SET_EMPLOYEE_COUNT, data.count); // 儲存總筆數
context.commit(SET_NEXT_URL, data.next); // 儲存下一頁的URL
context.commit(SET_PREV_URL, data.previous); // 儲存上一頁的URL
context.commit(SET_CURRENT_PAGE, params.page); // 更新目前頁碼
} catch (err) {
console.error("Failed fetching employees", err);
} finally {
context.commit(SET_LOADING, false); // 可用來控制畫面的載入動畫(結束)
}
},
};
// 對應的mutations
export const mutations = {
[SET_EMPLOYEES](state, payload) {
state.results = Array.isArray(payload) ? payload : [];
},
[SET_EMPLOYEE_COUNT](state, count) {
state.count = count;
},
[SET_NEXT_URL](state, url) {
state.next = url;
},
[SET_PREV_URL](state, url) {
state.previous = url;
},
[SET_CURRENT_PAGE](state, page) {
state.currentPage = page;
},
[SET_LOADING](state, flag) {
state.isLoading = flag;
},
};
// 對應的getters
export const getters = {
employees: (state) => state.results,
employeesCount: (state) => state.count,
pageSize: (state) => state.pageSize,
currentPage: (state) => state.currentPage,
totalPages: (state) => Math.ceil(state.results.length / state.pageSize) || 1,
nextPageUrl: (state) => state.next,
prevPageUrl: (state) => state.previous,
isLoading: (state) => state.isLoading,
};
export default {
namespaced: true,
state,
actions,
mutations,
getters
};
在這段Vuex模組程式碼中,大致可區分為呼叫actions(非同步)、commit mutations(同步)及state/getters驅動畫面。
在呼叫actions中,乃進行非同步操作,也就是先丟給API處理,等待其回傳值的過程中,畫面不會被定住。
更精確地說,dispatch()所觸發的action,內部用await走非同步I/O,不會阻塞UI的執行緒;同時因為使用SET_LOADING控制載入動畫,可確保使用者有回饋。
並且data乃是藉由EmployeeService.query(params)
向API請求來取得。
取得資料後,藉由一連串的commit來觸發對應的mutation函式,如SET_EMPLOYEES、SET_EMPLOYEE_COUNT、SET_NEXT_URL等,由mutation同步地改變state。值得注意的是,mutation是同步函式,負責直接修改state。
// 呼叫actions
export const actions = {
async [FETCH_EMPLOYEES](context, params) {
context.commit(SET_LOADING, true); // 可用來控制畫面的載入動畫(開始)
try {
const { data } = await EmployeeService.query(params); // 呼叫API拿資料
context.commit(SET_EMPLOYEES, data.results); // 儲存資料列表
context.commit(SET_EMPLOYEE_COUNT, data.count); // 儲存總筆數
context.commit(SET_NEXT_URL, data.next); // 儲存下一頁的URL
context.commit(SET_PREV_URL, data.previous); // 儲存上一頁的URL
context.commit(SET_CURRENT_PAGE, params.page); // 更新目前頁碼
} catch (err) {
console.error("Failed fetching employees", err);
} finally {
context.commit(SET_LOADING, false); // 可用來控制畫面的載入動畫(結束)
}
},
};
接著,定義多個與employee模組對應的mutation,每個mutation都對應一個特定的狀態更新動作
// 對應的mutations
export const mutations = {
[SET_EMPLOYEES](state, payload) {
state.results = Array.isArray(payload) ? payload : [];
},
[SET_EMPLOYEE_COUNT](state, count) {
state.count = count;
},
[SET_NEXT_URL](state, url) {
state.next = url;
},
[SET_PREV_URL](state, url) {
state.previous = url;
},
[SET_CURRENT_PAGE](state, page) {
state.currentPage = page;
},
[SET_LOADING](state, flag) {
state.isLoading = flag;
},
};
而當state被mutation改變時,getters,如employees, employeesCount, totalPages等,會根據最新state重新計算,提供給組件(component),經由mapGetters('employee', [...])
綁定後,畫面便自動響應更新。
// 對應的getters
export const getters = {
employees: (state) => state.results,
employeesCount: (state) => state.count,
pageSize: (state) => state.pageSize,
currentPage: (state) => state.currentPage,
totalPages: (state) => Math.ceil(state.results.length / state.pageSize) || 1,
nextPageUrl: (state) => state.next,
prevPageUrl: (state) => state.previous,
isLoading: (state) => state.isLoading,
};
到此,我們應該更能完全認識到Vuex的整體資料流,再次不厭其煩地說明,以為資料流的介紹做一個總結。
Vue組件 → dispatch呼叫action(非同步) → action內部發送API請求 → commit多個mutation(同步) → mutation改變state → getters根據最新state重新計算 → 綁定在組件的畫面自動響應更新