【问题标题】:Wait for VueX value to load, before loading component在加载组件之前等待 VueX 值加载
【发布时间】:2019-10-28 11:27:51
【问题描述】:

当用户尝试直接导航加载组件 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
    });
  }
}

如何让我的组件等待asyncListValues 加载,然后再加载我的组件?

【问题讨论】:

    标签: javascript vue.js vuejs2 vuex vue-router


    【解决方案1】:

    一种方法是将您的组件拆分为两个不同的组件。您的新父组件可以处理获取数据并在数据准备好后渲染子组件。

    父组件.vue

    <template>
      <child-component v-if="asyncListValues && asyncListValues.length" :asyncListValues="asyncListValues"/>
      <div v-else>Placeholder</div>
    </template>
    <script>
    export default {
      computed: {
        ...mapState({
          asyncListValues: state => state.asyncListValues
        })
      }
    }
    </script>
    

    ChildComponent.vue

    export default {
      props: ["asyncListValues"],
      mounted () {
        this.asyncListValues.forEach((val) => { 
          // do stuff
        });
      }
    }
    

    【讨论】:

    • 这似乎可行。如果我找不到其他任何东西,我可能会尝试这个。谢谢!
    【解决方案2】:

    由于您在问题中提到了vue-router,您可以使用beforeRouteEnter 来延迟组件的呈现。

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

    import Photo from "../page/Photo.vue";
    
    new VueRouter({
      mode: "history",
      routes: [
        { name: "home", path: "/", component: Home },
        { name: "photo", path: "/photo", component: Photo }
      ]
    });
    

    您可以像这样使用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>
    

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

    如果你需要一个没有 ES6 的例子,请告诉我(例如,如果你不使用这种语法)。

    您可以在此页面上查看official documentation of beforeRouteEnter,您还会发现您也可以使用beforeEnter 将其放在路由级别。

    【讨论】:

    • 'beforeRouteEnter 守卫无权访问这个,因为守卫在导航确认之前被调用,因此新的进入组件甚至还没有创建。 - router.vuejs.org/guide/advanced/…
    【解决方案3】:

    一种方法是存储状态值。

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

    您通常可以拥有 4 种状态,我更喜欢在全局可访问的模块中拥有这些状态:

    // enums.js
    export default {
      INIT: 0,
      LOADING: 1,
      ERROR: 2,
      LOADED: 3
    };
    

    然后,您可以将变量存储在 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))
        }
      }
    });
    

    然后,通过添加一些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>
    

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

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

    【讨论】:

    • 我认为这可能是最好的解决方案。但如果有人正在阅读本文,这里推荐一些其他好的解决方案
    • 是的,我也喜欢这个主意...我使用下面的路由器方法,但可以考虑此解决方案的用例...
    【解决方案4】:

    对我来说简单的方法:

    ...
    watch: {
       vuexvalue(newVal) {
          if (newVal == 'XXXX')
            this.loadData()
          }
       }
    },
    computed: {
       ...mapGetters(['vuexvalue'])
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-11-30
      • 2021-06-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-28
      相关资源
      最近更新 更多