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

1、問題的起源

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

2、第一種解決方法

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

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

咱們能夠經過state數據字段來實現,在state中存儲一個loading字段,並設置默認值爲false。axios

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

由於要對state.loading進行操做,因此,咱們須要定義一個mutation方法,用於更新loading狀態數據。promise

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便可。fetch

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

3、第二種解決方法

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

之前咱們知道,異步方法傳遞數據,能夠經過回調函數的參數進行傳遞數據,因此,我提到的第二種解決辦法就是經過回調函數實現的。code

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);
        });
    }
  },
  // ......
})

4、第三種解決方法

以上兩種方式雖然能夠解決某些問題,但解決方法不夠優雅,並且第一種方法具備很大的侷限性。好比,不能回調主界面中的方法執行後續的操做,也不能自由地傳遞參數。第二種方法採用回調能夠調用方法,也能夠傳參,但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 處理異常狀況
      });
  },
  // ...
}
相關文章
相關標籤/搜索