【问题标题】:Vue.prototype is undefined on first load of the pageVue.prototype 在页面首次加载时未定义
【发布时间】:2020-07-08 05:41:29
【问题描述】:

所以,我正在尝试在我的 main.js 文件中使用 axios 发出请求。

如图所示,我正在使用 vue-router 在加载每个组件之前发出此请求。但是,当我的网页第一次加载时,我无法让它工作。我的意思是,axios 请求是在组件加载后完成的。那么,这将失败:

mounted() {
    if (Vue.prototype.$user.role == "Owner") {
      this.isOwner = true;
      this.estancoId = Vue.prototype.$user.estanco;
    }
  },

它在控制台日志上显示了这个错误:

[Vue warn]: Error in mounted hook: "TypeError: Cannot read property 'role' of undefined"

found in

---> <Header> at src/components/Header.vue
       <App> at src/App.vue
         <Root>

我尝试使用 async/await 发出此请求,我尝试了 mounted(), created(), beforeMount(), beforeCreate() 方法,但仍然是一样的。我是 Vue.js 的新手,我被困在这里,不知道该怎么办。

编辑整个文件以查看应用结构: ma​​in.js

import router from './router'
import Vue from 'vue'
import App from './App.vue'
import { BootstrapVue, IconsPlugin } from 'bootstrap-vue'
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'
import axios from 'axios'
import Vuex from 'vuex'

// Install BootstrapVue
import 'leaflet/dist/leaflet.css';

Vue.use(BootstrapVue)
// Optionally install the BootstrapVue icon components plugin
Vue.use(IconsPlugin)
Vue.use(axios)
Vue.use(Vuex)
Vue.config.productionTip = false

const store = new Vuex.Store({
  state: {
    user : {}
  },
  mutations : {
    set_user (state,user) {
      state.user = user
    }
  }
})

export default store

/* eslint-disable */
router.beforeEach((to, from, next) => {
  if (from.path.indexOf("modificarCatalogo") == -1 && to.path.indexOf("modificarCatalogo") == -1) {
    localStorage.removeItem("catalogue");
  }
  if (localStorage.getItem("token") != null) {
    axios
      .get(`${process.env.VUE_APP_API_BASE_URL}/user/role`, {
        headers: {
          Authorization: "Token " + localStorage.getItem("token")
        }
      })
      .then(response => {
        store.commit('set_user', response.data);
        console.log("First then")
        console.log(store.state.user)
      }).catch(function (error) {
         // handle error case here
         console.log(error);

      }).then(function () {
         // always executed
         console.log("Second then")
         next();
      });
     }else{
        next();
     }
});
/* eslint-enable */

Vue.use(router)

new Vue({
  router,
  render: h => h(App),
}).$mount('#app')

它现在有 Vuex,因为我尝试了@ellisdod 的答案,但是

App.vue

<template>
  <div>
    <Header />
    <router-view />
    <Footer />
  </div>
</template>

并且,在 Header.vue 中,这是我调用 Vuex $store 的地方,但它是相同的。我需要它无处不在,所以我尝试调用App.vue中的方法但仍然没有结果,它现在使用Vuex的解决方案返回一个空对象,但只是空的,而不是用户数据。

export default {
  name: "Header",
  data() {
    return {
      token: localStorage.getItem("token"),
      isOwner: "",
      estancoId: ""
    };
  },
  mounted() {
    console.log("Header log")
    if (this.$store.state.user.role == "Owner") {
      this.isOwner = true;
      this.estancoId = this.$store.state.user.estanco;
    }
  },

我认为其余的组件无关紧要

【问题讨论】:

  • 使用 Vuex 存储用户信息:vuex.vuejs.org
  • 感谢您的帮助! Vuex 会在组件加载之前存储用户的状态吗?我的意思是,我认为这里的问题是Axios在组件加载后发出请求,所以我认为使用Vuex是一样的,不是吗?

标签: javascript vue.js vuejs2 vue-router


【解决方案1】:

嗨,

根据问题编辑、评论和答案完成答案:

问题

Vue-router 的 beforeEach 方法只会在路由中定义的组件中执行。
在你的情况下,

  1. beforeEach 不会在 Header 组件中调用,因为它不是路由的一部分。它是一个独立的组件。
    因此,您无法访问其中的$user

store.js

    import axios from 'axios'
    import Vuex from 'vuex'

    const store = new Vuex.Store({
    state: {
      user : {}
    },
    mutations : {
      set_user (state,user) {
        state.user = user
      }
    }
    actions : {
        loadUserFromLocal ({commit,state}) {
          if (localStorage.getItem("token") === null) return null
          return axios
            .get(`${process.env.VUE_APP_API_BASE_URL}/user/role`, {
            headers: {
              Authorization: "Token " + localStorage.getItem("token")
            }
          })
          .then(response => {
            commit('set_user', response.data);
            console.log("First then")
            console.log(state.user)
          }).catch(function (error) {
             // handle error case here
             console.log(error);
          })

        }
      }
    })
    export default store

@ellisdod - 谢谢。

现在您可以在组件中使用 store 中的用户变量,并在数据提取完成时更新或显示初始值直到那时

因此无需在 router.beforeEach 中获取数据

router.js

    // --> all imports
    import store from './store' // --> added
    //  --> all routes to be defined here... 

    router.beforeEach((to, from, next) => {
     // --> other logic for filtering routes
     // --> you can use 'store' variable here to get the user data and add 
    filtering 
    if (from.path.indexOf("modificarCatalogo") == -1 && 
    to.path.indexOf("modificarCatalogo") == -1) {
    localStorage.removeItem("catalogue");
     }
     next();

    });

正如你在 vue-router 中所知道的,如果调用 next 则表示导航被确认并且组件将被渲染。
有关在router.beforeEach 方法中使用存储变量的更多信息,请参阅this question

ma​​in.js

    import router from './router'

    import store from './store' // --> added

    import Vue from 'vue'
    import App from './App.vue'
    import { BootstrapVue, IconsPlugin } from 'bootstrap-vue'
    import 'bootstrap/dist/css/bootstrap.css'
    import 'bootstrap-vue/dist/bootstrap-vue.css'
    import axios from 'axios'
    import Vuex from 'vuex'
    // Install BootstrapVue
    import 'leaflet/dist/leaflet.css';

    Vue.use(BootstrapVue)
    // Optionally install the BootstrapVue icon components plugin
    Vue.use(IconsPlugin)
    Vue.use(axios)
    Vue.use(Vuex)
    Vue.config.productionTip = false


    Vue.use(router)

    new Vue({
      router,
      store    // --> added
      render: h => h(App),
    }).$mount('#app')


App.vue

mounted () {
    this.$store.dispatch('loadUserFromLocal')
  }

@ellisdod - 谢谢。


Header.vue

    export default {
      name: "Header",
      data() {
       return {
        token: localStorage.getItem("token")
       };
      },
      computed: {
       isOwner() {
         return this.$store.state.user.role == "Owner"
       }
       estancoId () {
         return this.$store.state.user.estanco;
       }
      }
      mounted() {
       console.log("Header log")
      },
    }

【讨论】:

  • 我猜这是一个非常好的方法,但它没有解决它,因为在组件加载后 axios 仍在完成,即使我们在 then 函数中调用 next 也是如此。我试图通过在 App.vue 中的路由器视图调用中添加 v-if 来等待它完成,但仍然无法正常工作。关于如何让它等待 axios 完成的任何猜测?
  • 能否通过登录控制台进行检查,确认 axios.get 后三个函数何时执行?即两个“then”调用和“catch”调用
  • 此外,如果未调用 next(),则不应加载您要导航到的组件
  • 是的,我只是为了确认。首先,我在每个调用中添加了一个 console.log,所以第一个调用是我组件上的挂载方法,然后是 beforeEach 方法上的第一个“then”,最后一个是 beforeEach 方法上的第二个 then。那是因为“axios”方法比我的组件挂载方法运行得晚,这就是问题所在。
  • 另外,哪个组件首先在路由器中作为“\home”路由加载?以及您在哪个组件中访问 $user
【解决方案2】:

如果你使用 Vuex 来存储你的用户数据,你可以用一个空对象预填充用户值,这样它就不会抛出错误。

const store = new Vuex.Store({
  state: {
    user : {}
  },
  mutations : {
    set_user (state,user) {
      state.user = user
    }
  },
  actions : {
    loadUserFromLocal ({commit,state}) {
      if (localStorage.getItem("token") === null) return null
      return axios
        .get(`${process.env.VUE_APP_API_BASE_URL}/user/role`, {
        headers: {
          Authorization: "Token " + localStorage.getItem("token")
        }
      })
      .then(response => {
        commit('set_user', response.data);
        console.log("First then")
        console.log(state.user)
      }).catch(function (error) {
         // handle error case here
         console.log(error);
      })

    }
  }
})

new Vue({
  router,
  store,
  render: h => h(App),
}).$mount('#app')

然后在你的主 App 组件的挂载钩子中添加:

mounted () {
    this.$store.dispatch('loadUserFromLocal')
  }

现在在您的路由器中,而不是在每条路由之前发出服务器请求,您只需检查存储:

if (this.$store.state.user.role) {
  // handle logged in user
}

【讨论】:

  • 我应该把“导出默认新的 Vuex.Store ....”方法放在哪里?我在 main.js 中尝试过,与应用 router.beforeEach 方法的地方相同,但是当我尝试this.$store.commit 时,出现错误:TypeError: Cannot read property '$store' of undefined
  • 您只需要在实例化 vue 时将其添加为选项之一 - 更新了我的答案
  • 它不再给出错误,但是,它在页面第一次加载时返回空对象,因为该方法尚未执行,所以它是空的。我尝试在created hook中的App.vue中运行它,但还是一样。
  • 我已经更新了我的答案,将服务器请求移动到商店并在应用程序挂载时分派它。如果您遇到商店无法加载的问题,这种安排可能会有所帮助:stackoverflow.com/a/45290290/6282604
  • 我希望我也可以选择您的答案作为解决方案...我将您的 Vuex 解决方案与 spc 提供的解决方案混合在一起,我解决了,非常感谢!!跨度>
猜你喜欢
  • 1970-01-01
  • 2019-10-30
  • 1970-01-01
  • 2015-03-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-14
  • 1970-01-01
相关资源
最近更新 更多