【问题标题】:Best practice for fetching data from api using vuex使用 vuex 从 api 获取数据的最佳实践
【发布时间】:2018-07-01 08:34:20
【问题描述】:

有两个页面(或相对于 vue 术语的组件)都需要相同的数据集,这些数据是通过 http 上的 api 提供的。访问这两个组件的顺序是未定义的(或者,取决于用户行为),并且数据应该只获取一次,因为它不会发生很大变化。

我知道state 存储实际数据的想法,mutations 对state 进行变异,actions 将脏工作作为异步请求、多变异协调等。

问题是:执行上述某些缓存逻辑的最佳做法是什么?

我想出了以下三种方法,但它们都不适合我:

缺点:

  1. 我需要在到处访问数据之前调度操作,因为我不知道数据是否已经被获取。

    // ComponentA
    async mouted () {
        await this.store.dispatch('fetchData')
        this.someData = this.store.state.someData
    }
    
    // ComponentB
    async mouted () {
        await this.store.dispatch('fetchData')
        this.someData = this.store.state.someData
    }
    
    // vuex action
    {
       async fetchData ({ state, commit }) {
           // handles the cache logic here
           if (state.someData) return
           commit('setData', await apis.fetchData())
       }
    }
    
  2. 缓存逻辑分散在整个代码库中——臭~

    // ComponentA
    async mouted () {
        if (this.store.state.someData === undefined) {
            // handles the cache logic
            await this.store.dispatch('fetchData')
        }
        this.someData = this.store.state.someData
    }
    
    // ComponentB
    async mouted () {
        if (this.store.state.someData === undefined) {
            // handles the cache logic
            await this.store.dispatch('fetchData')
        }
        this.someData = this.store.state.someData
    }
    
    // vuex action
    {
       async fetchData ({ state, commit }) {
           commit('setData', await apis.fetchData())
       }
    }
    
  3. 可能是这三个中最完美的一个,但是我觉得使用动作调度的返回值作为数据有点奇怪。随着商店的增长,缓存逻辑将分散在所有动作中(将会有越来越多的动作重复相同的缓存逻辑)

    // ComponentA
    async mouted () {
        this.someData = await this.store.dispatch('fetchData')
    }
    
    // ComponentB
    async mouted () {
        this.someData = await this.store.dispatch('fetchData')
    }
    
    // vuex action
    {
       async fetchData ({ state, commit }) {
           if (!state.someData) commit('setData', await apis.fetchData())
           return state.someData
       }
    }
    

我更喜欢将缓存逻辑放入我的“vue&vuex-independent”网络层。但随后网络层的“缓存”部分可能会变成另一个“vuex”存储。呵呵

【问题讨论】:

    标签: vue.js vuex


    【解决方案1】:

    我最近遇到了类似的问题。并且想通了,最好还是在你的行动中做出承诺。这有助于坚持vuex 生命周期。所以你的代码可能看起来像这样:

    {
        async fetchData ({ state, commit }) {
           if (!state.someData) commit('setData', await apis.fetchData())
           commit('setData', state.someData)
        }
     }
    

    然后使用getters 在您的组件中使用state.someData 而不是分配它。

    【讨论】:

      【解决方案2】:

      我觉得我正在重新发明轮子,应该有更好的现成的东西来做到这一点,但这是我的轮子,它对我来说很好。

      我回复是为了回馈,但也希望有人告诉我更好的方法!

      我的方法通过使用“加载”承诺而不是简单地检查是否加载了特定状态来解决避免多次获取的问题。如果您的 api 很慢和/或您的组件可能会在渲染之前多次调用 fetch 操作,这将帮助您将其保持为单个 api 调用。

        // vuex action
        async fetchCustomers({commit, state}) {
          await loadOrWait(loadingPromises, 'customers', async () => {
            // this function is called exactly once
            const response = await api.get('customers');
            commit('SET_CUSTOMERS', {customers : response.data });
          });
          return state.customers;
        },
      

      loaded 只是一个对象,它为每次获取存储一个承诺。我已经看到 someone 使用弱映射(如果内存使用是一个问题,那将是一个不错的选择)

        const loadingPromises = { 
          customers: false,
          customer: { }
        }
      

      loadOrWait 是一个简单的实用函数,它要么执行(恰好一次)一个函数(例如,从 api 获取)它看到一个获取正在进行中,所以它返回承诺之前的 fetch 调用。无论哪种情况,您都会得到一个承诺,它将解析 api 调用的结果。

      async function loadOrWait(loadingPromises, id, fetchFunction) {
        if (!loadingPromises[id]) {
          loadingPromises[id] = fetchFunction();
        }
        await loadingPromises[id];
      }
      

      也许您希望在获取时更加精细,例如,如果尚未获取特定客户,则获取他们。

        // vuex action 
        async fetchCustomer({commit, state}, customerId) {
          await loadOrWait(loadingPromises.customer, customerId, async () => {
            const response = await api.get(`customers/${customerId}`);
            commit('SET_CUSTOMER', { customerId, customer: response.data });
          });
          return state.customer[customerId];
        },
      

      其他一些想法:

      • 也许您不想永远缓存结果,在这种情况下,您可以在 loadOrWait 中获得创意,例如,类似
      setTimeout(()=>loadingPromises[id]=false,60*1000)
      
      • 也许您想定期刷新/轮询数据。让loadOrWait 保存函数也很简单!当你执行它们时,它会更新状态,如果你是一个优秀的 vue 编码器,你的应用程序会刷新。
      const fetchFunctionMap = {};
      
      async function loadOrWait(loadingPromises, id, fetchFunction) {
        if (!loadingPromises[id]) {
          loadingPromises[id] = fetchFunction();
          // save the fetch for later
          fetchFunctionMap[id] = fn;
        }
        await loadingPromises[id];
      }
      
      async function reload(loadingPromises, id) {
        if (fetchFunctionMap[id]){
           loadingPromises[id] = fetchFunctionMap[id]();
        }
        return await loadingPromises[id];
        // strictly speaking there is no need to use async/await in here.
      }
      
      //reload the customers every 5 mins
      setInterval(()=>reload(loadingPromises,'customers'), 1000 * 60 * 5);
      
      

      【讨论】:

        【解决方案3】:

        我知道我来这里很晚,但我希望我的方法可以为这个问题带来一个潜在的解决方案。这种方法实际上是我在许多项目中一直使用的方法,我发现它非常有效且易于遵循。

        我的想法基本上是使用API factory/service 而不是Vuex action。每次您需要获取数据时,您都会调用从 API 工厂公开的服务。该服务将检查您需要的数据是否已经在商店中,否则从 API 中检索。

        是这样的:

        // Vuex store
        const store = new Vuex.Store({
          state: {
            someData: {}
          },
          mutations: {
            SET_SOME_DATA (state, payload) {
              state.someData = payload
            }
          }
        })
        
        // API factory/service
        import store from 'yourStoreDirectory'
        
        export async getSomeData() {
          if (store.state.someData !== undefined) {
            return store.state.someData
          }
        
          try {
            // fetch API
        
            // store data into Vuex for next time
            store.commit('SET_SOME_DATA', response)
          } catch (err) {
             // handle errors
          }
        }
        
        // Component
        import { getSomeData } from 'yourAPIFactoryDirectory'
        
        export default {
          async mounted () {
            this.someData = await getSomeData()
          }
        }
        

        所以每次你使用这个getSomeData 服务时,你不必考虑是从商店还是API 获取。这种方式使您的代码更清晰,更具可读性。

        我个人不太使用action,一般来说,我总是尽量让我的Vuex 尽可能简单,就像它的标题一样——状态管理,而不是数据处理或业务逻辑管理。你可以参考这篇文章来了解更多关于这个想法,https://javascript.plainenglish.io/stop-using-actions-in-vuex-a14e23a7b0e6

        【讨论】:

          猜你喜欢
          • 2021-12-24
          • 2021-06-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-07-23
          • 1970-01-01
          • 2016-08-26
          • 2021-06-05
          相关资源
          最近更新 更多