前端技術之:如何在vuex狀態管理action異步調用結束後執行UI中的方法

  • 2019 年 11 月 8 日
  • 筆記

一、問題的起源

最近在做vue.js項目時,遇到了vuex狀態管理action與vue.js方法互相通信、互操作的問題。場景如下圖所示:

二、第一種解決方法

例如,我們在頁面初始化的時候,需要從服務端通過API接口獲取數據,數據獲取成功前需要顯示Loading狀態框,數據獲取完成後,需要將Loading狀態框隱藏。

這是一種相對比較簡單的應用場景,解決起來當然也比較簡單。

我們可以通過state數據字段來實現,在state中存儲一個loading字段,並設置默認值為false。

const store = new Vuex.Store({    state: {      loading: false    },    // ......  });  

因為要對state.loading進行操作,所以,我們需要定義一個mutation方法,用於更新loading狀態數據。

const UPDATE_LOADING = 'updateLoading';    const store = new Vuex.Store({    // ......,    mutations: {      updateLoading (state, loading) {        state.loading = loading;      }    },    // ......  });  

然後,我們聲明一個action方法,用於從HTTP API獲取數據。

const store = new Vuex.Store({    // ......,    actions: {      fetchData ({ commit }) {        commit(UPDATE_LOADING, true);        axios.get('...', { params: {...} })          .then(res => {            // TODO 解析HTTP響應數據,進行相關的業務邏輯處理          })          .catch(err => {            // TODO 進行相關的錯誤與異常處理          })          .finally(() => {            commit(UPDATE_LOADING, false);          });      }    },    // ......  })  

在頁面模板中,我們通過mapActions函數將vuex的action方法映射為vue.js中對象的方法。

import { mapActions } from 'vuex'    export default {    // ...    methods: {      ...mapActions([        'fetchData',        // ...      ])    }  }  

最後,在vue.js的mounted生命周期方法中調用通過mapActions映身的方法fetchData即可。

export default {    // ...,    mounted() {      this.fetchData();    },    // ...  }  

三、第二種解決方法

上述的第一種解決方法,可以通過mutation修改state的狀態數據控制UI上的數據渲染。但如果想要將獲取到的結果數據傳到UI組件是不行的,另外,如果想要在調用action方法執行完成後在UI中再去做一些事情也是行不通的。

以前我們知道,異步方法傳遞數據,可以通過回調函數的參數進行傳遞數據,所以,我提到的第二種解決辦法就是通過回調函數實現的。

const store = new Vuex.Store({    // ......,    actions: {      fetchData ({ commit }, { params, callback }) {        commit(UPDATE_LOADING, true);        axios.get('...', { params })          .then(res => {            callback(res);          })          .catch(err => {            // TODO 進行相關的錯誤與異常處理          })          .finally(() => {            commit(UPDATE_LOADING, false);          });      }    },    // ......  })  

四、第三種解決方法

以上兩種方式雖然可以解決某些問題,但解決方法不夠優雅,而且第一種方法具有很大的局限性。比如,不能回調主界面中的方法執行後續的操作,也不能自由地傳遞參數。第二種方法採用回調可以調用方法,也可以傳參,但callback的調用是同步方式,代碼風格也不是很好。所以,我比較提倡大家使用第三種方法,就是在action調用時返回一個Promise,這樣在主界面就可以拿到這個promise對象,並進行鏈式執行後續的任務,也可以將action異步任務的結果數據傳遞給主UI。

const store = new Vuex.Store({    // ......,    actions: {      fetchData ({ commit }, { params }) {        commit(UPDATE_LOADING, true);        return axios.get('...', { params })          .then(res => {            const { data } = res;            return data;          })          .finally(() => {            commit(UPDATE_LOADING, false);          });      }    },    // ......  })  

在主UI中,我們就可以採用如下的方式進行後續的操作。

export default {    // ...,    mounted() {      this.fetchData({ id: 1 })        .then(res => {          // TODO 執行後續的任務        })        .catch(err => {          // TODO 處理異常情況        });    },    // ...  }