【问题标题】:Vuex rendering data that is fetched from REST API从 REST API 获取的 Vuex 渲染数据
【发布时间】:2017-05-27 07:44:58
【问题描述】:

对于这样的组件

<template>
  <div>
    <router-link :to="{name:'section', params: { sectionId: firstSectionId }}">Start</router-link>
  </div>
</template>
    
<script lang="ts">
  import { mapActions } from "vuex"
    
  export default {
    mounted() {
      this.getSectionId()
    },
    computed: {
      firstSectionId() {
        return this.$store.state.firstSectionId
      }
    },
    methods: mapActions(["getSectionId"])
  }
</script>

商店:

const store: any = new Vuex.Store({
    state: {
        firstSectionId: null
    },
    // actions,
    // mutations
})

我在 getSectionId 操作中有一个 Web 请求,它异步获取数据并调用将填充 firstSectionIdstate 的突变。在初始渲染期间,firstSectionIdnull,我收到警告说在渲染router-link 期间缺少必需的参数。

这里添加v-if="firstSectionId"是没有问题的。但总的来说,从服务器获取数据以显示的方法是什么?目前我所有的组件都在渲染之前检查存储中是否存在数据,这是正常的还是有更好的方法在渲染之前等待数据加载?

【问题讨论】:

  • 在渲染之前等待数据加载,您使用服务器端渲染。除此之外,为什么要“等待数据”?我们每天都会看到网站和应用程序使用“加载”指示器加载静态页面结构,然后在它们到达时用数据填充它们。显然,尽可能早地向用户展示是最好的体验。

标签: vue.js vuex


【解决方案1】:

异步获取数据的一种方法是在 vuex 存储 actions 中使用 promise

Vue.http.get(API_URL)
  .then((response) => {
     //use response object      
  })
  .catch((error) => {
    console.log(error.statusText)
  });

为了证明我向this route 提出请求。您可以看到响应应该是什么样子。让我们将响应对象保存在 state.users 数组中。

store.js

const store = new Vuex.Store({
  state: {
    users: []
  },  
  mutations: {
    FETCH_USERS(state, users) {
      state.users = users
    }
  },
  actions: {
    fetchUsers({ commit }, { self }) {          
      Vue.http.get("https://jsonplaceholder.typicode.com/users")
        .then((response) => {
          commit("FETCH_USERS", response.body);
          self.filterUsers();   
        })
        .catch((error) => {
          console.log(error.statusText)
        });
    }
  }
})
    
export default store

您注意到提交后有self.filteruser() 方法。那是关键时刻。在此之前,我们提交一个突变,这是同步操作,我们确信我们的响应会在 store.state 中,可以在filterUsers() 方法中使用(别忘了传递自我参数)

Users.vue

import store from "../store/store"

export default {
  name: 'users',
  created() {
    this.$store.dispatch("fetchUsers", { self: this })       
  },
  methods:{
    filterUsers() {
      //do something with users
      console.log("Users--->",this.$store.state.users)       
    }
  }
}

更好的方法(ES6 和 ES7)

用于异步编程的 ES6 Promise

//User.vue
created() {
  this.$store.dispatch("fetchUser").then(() => {
    console.log("This would be printed after dispatch!!")
  })
}

//store.js
actions: {
  fetchUser({ commit }) {
    return new Promise((resolve, reject) => {
      Vue.http.get("https://jsonplaceholder.typicode.com/users")
        .then((response) => {
          commit("FETCH_USERS", response.body);
          resolve();
         })
         .catch((error) => {
           console.log(error.statusText);
         });
    });
  }
}

ES7:异步/等待

要摆脱回调地狱,并改进异步编程,请使用 async 函数,您可以在 Promise 上使用 await。代码看起来更容易理解(就像它是同步的一样),但是代码对于浏览器来说是不可读的,所以你需要 Babel transpiler 来运行它。

actions: {
  async actionA ({ commit }) {
    commit('gotData', await getData())
  },
  async actionB ({ dispatch, commit }) {
    await dispatch('actionA') // wait for actionA to finish
    commit('gotOtherData', await getOtherData())
  }
}

【讨论】:

  • 没有什么对我有用。我正在尝试从 WordPress API 获取内容,但页面加载时间约为 40 毫秒,并且在页面加载后发送 API 调用。我正在使用服务器端渲染并希望在页面加载时渲染内容。 Vuex 商店没有帮助
  • ES6 的方式帮助了我。
【解决方案2】:

根据我的经验,如果您使用与预期结果相同类型的空值预设状态(当然,如果您知道会发生什么),例如,您可以跳过一些检查。如果您有一组项目,请以 [] 而不是 null 开头,因为它不会破坏 v-for 指令、.length 检查和类似的数据访问尝试。

但一般来说,添加v-if 是很正常的事情。有a section about this in the vue-router documentation,检查属性是否存在正是它所暗示的。它提到的另一种可能的解决方案是在beforeRouteEnterguard 中获取数据,这可以确保您始终可以使用已经可用的数据访问组件。

最终,两种解决方案都是正确的,它们之间的决定更多的是一个 UX/UI 问题。

【讨论】:

    【解决方案3】:

    我对位置和谷歌地图 api 有类似的要求。我需要从 API 中获取我的位置,将它们加载到列表中,然后在地图组件中使用这些位置来创建标记。我使用 axios 在 Vuex 操作中获取数据,在我的状态下使用突变加载数据,然后使用 getter 在已安装的生命周期挂钩中检索结果数组。这导致在异步操作解析之前触发了一个空数组。

    我用 store.subscribe 来解决这个问题:

    <template>
      <div class="google-map" :id="mapName"></div>
    </template>
    
    <script>
    import GoogleMapsLoader from 'google-maps';
    import { mapGetters } from 'vuex';
    
    export default {
      name: 'google-map',
      props: ['name'],
      computed: {
        ...mapGetters({
          locations: 'locations/locations',
        }),
      },
      data() {
        return {
          mapName: `${this.name}-map`,
        };
      },
      mounted() {
        this.$store.subscribe((mutation, state) => {      
          if (mutation.type === 'locations/SAVE_LOCATIONS') {
            GoogleMapsLoader.KEY = 'myKey';
            GoogleMapsLoader.load((google) => {
              /* eslint-disable no-new */
              const map = new google.maps.Map(document.getElementById('locations-map'));
    
              // loop through locations and add markers to map and set map boundaries
              const bounds = new google.maps.LatLngBounds();
    
              // I access the resulting locations array via state.module.property
              state.locations.locations.forEach((location) => {
                new google.maps.Marker({
                  position: {
                    lat: location.latitude,
                    lng: location.longitude,
                  },
                  map,
                });
                bounds.extend({
                  lat: location.latitude,
                  lng: location.longitude,
                });
              });
    
              map.fitBounds(bounds);
            });
          }
        });
      },
    };
    

    【讨论】:

    • 谢谢你,这真的帮助了我。在初始页面加载时保持空白存储并使用计算值没有帮助。只有 $store.subscribe 工作,我不明白 为什么 但我很感激。你知道为什么它比计算效果更好吗?
    • 这是几年前的事了,现在看,我应该在加载地图之前获取驱动标记的数据。那会干净得多。
    猜你喜欢
    • 1970-01-01
    • 2019-04-13
    • 2020-06-26
    • 2019-04-28
    • 2018-02-19
    • 2021-01-18
    • 1970-01-01
    • 1970-01-01
    • 2021-01-18
    相关资源
    最近更新 更多