首页 文章

如何使用Vuex / Vue处理异步数据检索

提问于
浏览
5

我有一个带有共同堆栈的简单应用程序:

  • 后端服务器(Rails)

  • 前端应用程序(Vue)

  • 数据库(PG)

Vue应用程序使用Vuex存储库的操作从后端获取数据,如下所示:

// store/store.js
import Vue from 'vue';
import Vuex from 'vuex';
import * as MutationTypes from '@/store/mutation-types';

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    investment: {},
  },
  mutations: {
    [MutationTypes.SET_INVESTMENT_SHOW](state, investment) {
      state.investment = investment;
    },
  },
  actions: {
    fetchInvestment({ commit }, id) {
      InvestmentsApi.get(id).then((response) => {
        commit(MutationTypes.SET_INVESTMENT_SHOW, response.data);
      });
    },
  },
  getters: {
    participation: state =>
      state.investment.included[0],
  },
});

在我的组件的创建生命周期钩子中调用该操作,如下所示:

// components/Investment.vue

import { mapActions, mapGetters } from 'vuex';
export default {
  name: 'Investment',
  computed: {
    ...mapState(['investment']),
    ...mapGetters(['participation']),
  },
  created() {
    this.fetchData(this.$route.params.id);
  },
  methods: mapActions({
    fetchData: 'fetchInvestment',
  }),
};

我在上面编写的代码中存在一个问题,我实际上在我的模板中使用计算值'参与',如下所示:

<BaseTitleGroup
  :subtitle="participation.attributes.name"
  title="Investissements"
/>

当然,因为我在组件渲染时使用了参与,所以我从getter方法得到了这个错误:

Error in render: "TypeError: Cannot read property '0' of undefined"

found in

---> <InvestmentSummary> at src/views/InvestmentSummary.vue
       <App> at src/App.vue
         <Root>

我认为有几种解决方案可以解决这个问题,我想知道哪一个是最佳实践,或者是否有更好的实践 .

  • 第一个解决方案是在我的模板中放置一个v-if属性,以防止在等待数据时呈现元素

  • Con:渲染偏移(当数据存在时,元素开始渲染)?

  • Con:我必须为处理异步数据的应用程序的每个组件执行此操作,直觉上我更愿意在其他地方处理此问题(可能是商店?) .

  • 渲染元素并将假数据放入商店,如"Loading..."

  • Con:当文本从加载切换到真实文本时,用户在加载页面时看到的小故障很难看 .

  • Con:当我的应用扩展时,我的商店的空版本会很难写,超级大

  • 更改getter以返回初始空数据,而不是商店

  • Con:Getters变得更复杂

  • Con:那些不需要getter的数据(也许可以从州直接访问)

  • 别的什么?

我正在寻找处理这种模式的最佳解决方案,即使它是上述之一,我只是不确定哪一种是最好的 . 非常感谢阅读!另外,我使用vue框架,但我认为这是关于现代javascript框架处理异步数据和渲染的一般问题 .

对不起,很长的帖子,这是一个土 beans ! (糟糕,不是9gag;))

2 回答

  • 1

    我不认为有最好的解决方案,只需选择一个并在任何地方使用它而不是将它们全部混合 .

    v-if 但是如果你想从嵌套属性渲染数据可能会更好 - v-if="object.some.nested.property v-for="el in object.some.nested.property" 将起作用但预定义 object = {} 将不会(它将抛出一个错误, some 未定义,你试图访问它) .

    我不会在您的示例中添加任何虚假数据,但您可以使用ES6 Classes来定义默认对象并将它们设置为默认值 . 只要您的类对象具有适当的结构(并且它在语法上也是透明的且易于理解),这也解决了上述预定义问题 .

    至于第三种选择 - 将空数据提供给getter并不一定要复杂 - 只需将你的getter更改为:

    getters: {
        participation: state =>
          state.investment.included[0] || new DefaultParticipationObject() // I don't know what's in included array
      },
    

    如果已定义,则使用 state.investment.included[0] ,否则使用默认对象 .

  • 1

    在Angular中,有Elvis(安全导航)操作符,它是处理 eventually 到达的反应数据的简明方法 .

    如果它在Vue模板编译器中可用,那么您的模板将如下所示:

    <BaseTitleGroup
      :subtitle="participation?.attributes?.name"
      title="Investissements"
    />
    

    但是,Evan You说it sounds like a code smell .

    您的模型/状态应尽可能可预测 .

    试图将该评论扩展到您的上下文中,我认为这意味着 your template knows more about your data structure than your store .

    template

    "participation.attributes.name"
    

    这相当于:

    state.investment.included[0].attributes.name
    

    store

    state: {
      investment: {},
    },
    

    由于getter用于提供Component(以及它的模板),我会选择增强getter .

    getters: {
      participation_name: state => {
        return 
          (state.investment.included 
           && state.investment.included.length
           && state.investment[0]
           && state.investment[0].attributes
           && state.investment[0].attributes.name)
          || null;
    },
    
    <BaseTitleGroup
      :subtitle="participation_name"
      title="Investissements"
    />
    

    但是如果你想要elvis功能,你可以在mixin中提供它 .

    var myMixin = {
      computed: {
        elvis: {
          get: function() {
            return (known, unknown) => {
              // use regex split to handle both properties and array indexing
              const paths = unknown.split(/[\.(\[.+\])]+/); 
              let ref = known
              paths.forEach(path => { if (ref) ref = ref[path] });
              return ref;
            }
          }
        },
      }
    }
    
    export default {
      name: 'Investment',
      ...
      mixins: [myMixin],
      computed: {
        ...mapState(['investment']),
        participation_name() {
          return this.elvis(this.investment, 'included[0].attributes.name')
        }
      },
      ...
    };
    

相关问题