从 REST API 获取的 Vuex 渲染数据

Posted

技术标签:

【中文标题】从 REST API 获取的 Vuex 渲染数据【英文标题】:Vuex rendering data that is fetched from REST API 【发布时间】: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"是没有问题的。但总的来说,从服务器获取数据以显示的方法是什么?目前我所有的组件都在渲染之前检查存储中是否存在数据,这是正常的还是有更好的方法在渲染之前等待数据加载?

【问题讨论】:

在渲染之前等待数据加载,您使用服务器端渲染。除此之外,为什么要“等待数据”?我们每天都会看到网站和应用程序使用“加载”指示器加载静态页面结构,然后在它们到达时用数据填充它们。显然,尽可能早地向用户展示是最好的体验。 【参考方案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 工作,我不明白 为什么 但我很感激。你知道为什么它比计算效果更好吗? 这是几年前的事了,现在看,我应该在加载地图之前获取驱动标记的数据。那会干净得多。

以上是关于从 REST API 获取的 Vuex 渲染数据的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 rest API 在应用程序启动时正确初始化 Vuex 商店?

Vue.js - 从 API 获取数据并在 Vuex 状态更改时重新加载组件

Angular Material - mat-table 不渲染来自 rest api 的数据

在渲染之前将 ajax 获取的数据设置为组件

无法从 ArcGIS REST API 获取形状数据

如何从具有基本身份验证的 rest api 获取数据?