【问题标题】:How to deal with async data retrieval with Vuex / Vue如何用 Vuex / Vue 处理异步数据检索
【发布时间】:2019-02-25 13:49:18
【问题描述】:

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

  • 后端服务器 (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>

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

  1. 第一个解决方案是在我的模板中放置一个 v-if 属性,以防止元素在等待数据时呈现
    • Con : 渲染偏移量(元素在数据存在时开始渲染)?
    • 缺点:我必须为我的应用程序中处理异步数据的每个组件都这样做,直觉上我更愿意在其他地方处理这个问题(也许是商店?)。
  2. 渲染元素并将假数据放入存储中,例如“正在加载...”
    • 缺点:当文本从加载切换到真实文本时,用户在加载页面时看到的小故障很难看。
    • 缺点:当我的应用程序扩展时,我的商店的空版本写起来会很痛苦,而且超级大
  3. 更改 getter 以返回初始空数据,而不是 store
    • 缺点:Getter 变得更加复杂
    • 缺点:不需要 getter 的数据怎么办(也许它们可以直接从状态访问)
  4. 还有别的吗?

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

抱歉,帖子太长了,这是一个土豆! (糟糕,不在 9gag 上;))

【问题讨论】:

  • 在我看来,第三种方法是最干净的,因为使用 getter 永远不会导致错误。我会做类似的事情: state.investment.included[0] || ""

标签: javascript vue.js vuejs2 vuex


【解决方案1】:

在 Angular 中,有 Elvis(安全导航)运算符,这是一种处理最终到达的反应数据的简洁方法。

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

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

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

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

试图将该评论扩展到您的上下文中,我认为这意味着您的模板比您的商店更了解您的数据结构

模板

"participation.attributes.name"

相当于:

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

商店

state: {
  investment: {},
},

既然 getter 是为组件服务的(它是模板),我会选择增强 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')
    }
  },
  ...
};

【讨论】:

    【解决方案2】:

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

    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],否则使用默认对象。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-03-02
      • 1970-01-01
      • 1970-01-01
      • 2021-12-18
      • 2018-07-21
      • 2015-07-02
      • 2017-01-11
      • 2014-02-13
      相关资源
      最近更新 更多