在加载组件之前等待 VueX 值加载

Edo*_*don 4 javascript vue.js vue-router vuex vuejs2

当用户尝试直接导航加载组件 url 时,我的 vuex 操作中会进行一个 http 调用,一旦它解析,它将在我的状态中定义一个值。

在解析 http 调用并定义状态值之前,我不想加载我的组件。

例如,在我的组件中

export default {
  computed: {
    ...mapState({
      // ** this value needs to load before component mounted() runs **
      asyncListValues: state => state.asyncListValues
    })
  },

  mounted () {
    // ** I need asyncListValues to be defined before this runs **
    this.asyncListValues.forEach((val) => { 
      // do stuff
    });
  }
}
Run Code Online (Sandbox Code Playgroud)

asyncListValues在加载我的组件之前,如何让我的组件等待加载?

Dan*_*iel 10

一种方法是存储状态值。

例如,如果您的商店依赖于单个 API,您将执行以下操作。但是,对于多个 API,最好单独存储每个 api 加载状态,或者为每个 API 使用一个专用对象。

通常你可以拥有 4 种状态,我更喜欢在全局可访问模块中拥有它们:

// enums.js
export default {
  INIT: 0,
  LOADING: 1,
  ERROR: 2,
  LOADED: 3
};
Run Code Online (Sandbox Code Playgroud)

然后,您可以将变量存储在 vuex 状态中,其中 apiState 使用INIT. 您也可以使用 初始化数组[],但这不是必需的。

import ENUM from "@/enums";
// store.js
export default new Vuex.Store({
  state: {
    apiState: ENUM.INIT,
    accounts: [],
    // ...other state
  },
  mutations: {
    updateAccounts (state, accounts) {
      state.accounts = accounts;
      state.apiState = ENUM.LOADED;
    },
    setApiState (state, apiState) {
      state.apiState = apiState;
    },
  },
  actions: {
    loadAccounts ({commit) {
      commit('setApiState', ENUM.LOADING);
      someFetchInterface()
        .then(data=>commit('updateAccounts', data))
        .catch(err=>commit('setApiState', ENUM.ERROR))
    }
  }
});
Run Code Online (Sandbox Code Playgroud)

然后,通过添加一些computed变量,您可以切换显示哪个组件。使用状态的好处是可以轻松识别错误状态,并在状态未就绪时显示加载动画。

<template>
  <ChildComponent v-if="apiStateLoaded"/>
  <Loader v-if="apiStateLoading"/>
  <Error v-if="apiStateError"/>
</template>
<script>
import ENUM from "@/enums";
export default {
  computed: {
    ...mapState({
      apiState: state=> state.apiState
    }),
    apiStateLoaded() {
      return this.apiState === ENUM.LOADED;
    },
    apiStateLoading() {
      return this.apiState === ENUM.LOADING || this.apiState === ENUM.INIT;
    },
    apiStateError() {
      return this.apiState === ENUM.ERROR;
    },
  })
}
</script>
Run Code Online (Sandbox Code Playgroud)

除了...我使用这种模式来管理我的应用程序作为一个状态机。虽然这个例子使用了 vuex,但它可以适应在组件中使用,使用Vue.observable(vue2.6+) 或ref(vue3)。

或者,如果您只是asyncListValues使用空数组在 store 中初始化[],则可以避免期望数组的错误。


Anw*_*war 5

vue-router既然您在问题中提到,您可以使用beforeRouteEnterwhich 来推迟组件的渲染。

例如,如果您有一条名为“照片”的路线:

import Photo from "../page/Photo.vue";

new VueRouter({
  mode: "history",
  routes: [
    { name: "home", path: "/", component: Home },
    { name: "photo", path: "/photo", component: Photo }
  ]
});
Run Code Online (Sandbox Code Playgroud)

beforeRouteEnter你可以这样使用:

<template>
  <div>
    Photo rendered here
  </div>
</template>
<script>
export default {
  beforeRouteEnter: async function(to, from, next) {
    try {
      await this.$store.dispatch("longRuningHttpCall");

      next();
    } catch(exception) {
      next(exception);
    }
  }
}
</script>
Run Code Online (Sandbox Code Playgroud)

它的作用是,等待操作完成,根据需要更新状态,然后调用next()将告诉路由器继续该过程(在 内渲染组件<router-view></router-view>)。

告诉我您是否需要 ES6-less 示例(例如,如果您不使用此语法)。

您可以查看此页面上的官方文档beforeRouteEnter,您还会发现您也可以使用将其放在路由级别beforeEnter