定义Vue-Router路由时访问Vuex状态

时间:2017-03-05 02:48:44

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

我有以下Vuex商店(main.js):

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

//init store
const store = new Vuex.Store({
    state: {
        globalError: '',
        user: {
            authenticated: false
        }
     },
     mutations: {
         setGlobalError (state, error) {
             state.globalError = error
         }
     }
})

//init app
const app = new Vue({
    router: Router,
    store,
    template: '<app></app>',
    components: { App }
}).$mount('#app')

我还为Vue-Router(routes.js)定义了以下路由:

import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)

//define routes
const routes = [
    { path: '/home', name: 'Home', component: Home },
    { path: '/login', name: 'Login', component: Login },
    { path: '/secret', name: 'Secret', component: SecretPage, meta: { requiresLogin: true }
]

我试图这样做,以便如果Vuex商店的user对象的authenticated属性为false,请让路由器将用户重定向到“登录”页面。

我有这个:

Router.beforeEach((to, from, next) => {
    if (to.matched.some(record => record.meta.requiresLogin) && ???) {
        // set Vuex state's globalError, then redirect
        next("/Login")
    } else {
        next()
    }
})

问题是我不知道如何从beforeEach函数中访问Vuex商店的user对象。

我知道我可以使用BeforeRouteEnter在组件内部使用路由器保护逻辑,但这会使每个组件混乱。我想在路由器级别集中定义它。

9 个答案:

答案 0 :(得分:29)

根据建议here,您可以执行的操作是从您所在的文件中导出商店,然后将其导入 routes.js 。它将类似于以下内容:

您有一个 store.js

import Vuex from 'vuex'

//init store
const store = new Vuex.Store({
    state: {
        globalError: '',
        user: {
            authenticated: false
        }
     },
     mutations: {
         setGlobalError (state, error) {
             state.globalError = error
         }
     }
})

export default store

现在在 routes.js 中,你可以拥有:

import Vue from 'vue'
import VueRouter from 'vue-router'
import store from ./store.js

Vue.use(VueRouter)

//define routes
const routes = [
    { path: '/home', name: 'Home', component: Home },
    { path: '/login', name: 'Login', component: Login },
    { path: '/secret', name: 'Secret', component: SecretPage, meta: { requiresLogin: true }
]

Router.beforeEach((to, from, next) => {
    if (to.matched.some(record => record.meta.requiresLogin) && ???) {
        // You can use store variable here to access globalError or commit mutation 
        next("/Login")
    } else {
        next()
    }
})

main.js 中,您也可以导入store

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

import store from './store.js'

//init app
const app = new Vue({
    router: Router,
    store,
    template: '<app></app>',
    components: { App }
}).$mount('#app')

答案 1 :(得分:6)

您可以使用 router.app 访问路由器注入的根 Vue 实例,然后通过 router.app.$store 定期访问 store。

const router = new Router({
    routes,
})

router.beforeEach((to, from, next) => {
    // access store via `router.app.$store` here.
    if (router.app.$store.getters('user')) next();
    else next({ name: 'login' });
})

这是API Reference

答案 2 :(得分:4)

按照@Saurabh的建议将商店导入。但是,恕我直言,它会在您的代码中带来某种解决方法的气味。

它有效,因为Vuex商店是单例。导入它会在您的组件,路由器和存储之间创建硬链接依赖关系。至少它使单元测试变得更加困难。 vue-router被解耦并如此工作是有原因的,它可能会遵循其建议的模式并保持路由器与实际商店实例的解耦状态。

看看vue-router的来源,很明显,有一种更优雅的方式可以从路由器访问商店,例如在beforeRouteEnter卫队中:

beforeRouteEnter: (to, from, next) => {
  next(vm => {
    // access any getter/action here via vm.$store
    // avoid importing the store singleton and thus creating hard dependencies
  })
}

于2020年9月10日进行编辑(感谢@Andi指出)

然后根据具体情况使用beforeRouteEnter保护罩。即时开始,我看到以下选项:

  1. mixin 中声明防护,并在需要它的组件中有选择地使用它,而不是在全局防护中过滤所需的组件
  2. 全局mixin 中声明警卫(请注意声明的特殊性,例如,需要在Vue.use(VueRouter);herehere之后声明)

答案 3 :(得分:3)

管理您的位置状态与其他应用程序状态分开可能会使这样的事情变得比他们可能需要的更难。在处理Redux和Vuex中的类似问题后,我开始使用router模块管理我的Vuex商店内的位置状态。您可能想要考虑使用该方法。

在您的特定情况下,您可以查看Vuex商店内的位置何时发生变化,并调度相应的&#34;重定向&#34;行动,像这样:

dispatch("router/push", {path: "/login"})

比您想象的更容易将位置状态作为Vuex模块进行管理。如果您想尝试一下,可以使用我的作为起点:

https://github.com/geekytime/vuex-router

答案 4 :(得分:3)

我发现使用防护路由器.beforeEach时,在router.py中无法使用该存储,但是通过更改 保护router.beforeResolve,然后存储可用。

我还发现,通过等待在保护路由器中导入存储router.beforeEach,我便能够成功使用router.beforeEach。我在router.beforeResolve代码下面提供了一个示例。

因此,为了使我的示例与OP的问题保持相似,以下是它对我来说将如何工作。我正在使用vue-router 3.0.2和vuex 3.1.0。

import Vue from 'vue'
import VueRouter from 'vue-router'
import store from '@/store';  //or use a full path to ./store 

Vue.use(VueRouter)

//define routes
const routes = [
    { path: '/home', name: 'Home', component: Home },
    { path: '/login', name: 'Login', component: Login },
    { path: '/secret', name: 'Secret', component: SecretPage, meta: { requiresLogin: true }
]

const router = new VueRouter({
   routes  //es6
 })

router.beforeResolve((to, from, next) => {
    const user = store.state.user.user;  //store with namespaced  modules
    if (to.matched.some(record => record.meta.requiresLogin) && user.isLoggedIn) {
       next() //proceed to the route
    } else next("/login")  //redirect to login

})

export default router;

我还发现,可以通过在beforeEach守护程序中等待商店的加载来使router.beforeEach正常工作。

router.beforeEach(async (to, from, next) => {
  const store = await import('@/store');  //await the store 
  const user = store.state.user.user;  //store with namespaced modules
  if (to.matched.some(record => record.meta.requiresLogin) && user.isLoggedIn) {
  ....  //and continue as above
});

答案 5 :(得分:1)

我最终将商店从main.js移到store / index.js,并将其导入router.js文件:

import store from './store'

//routes

const routes = [
    { path: '/home', name: 'Home', component: Home },
    { path: '/login', name: 'Login', component: Login },
    { path: '/secret', name: 'Secret', component: SecretPage, meta: { requiresLogin: true }
]    

//guard clause
Router.beforeEach((to, from, next) => {
    if (to.matched.some(record => record.meta.requiresLogin) && store.state.user.authenticated == false) {
        store.commit("setGlobalError", "You need to log in before you can perform this action.")
        next("/Login")
    } else {
        next()
    }
})

答案 6 :(得分:0)

这就是我想要的。

在App.vue中,我会在cookie上保存一个存储身份验证详细信息的观察程序。 (显然,我会在身份验证后将包含身份验证详细信息的令牌存储为cookie)

现在每当这个cookie变空时,我都会将用户路由到/ login页面。注销会删除cookie。现在,如果用户在注销后回复,现在由于cookie不存在(需要用户登录),用户将被路由到登录页面。

答案 7 :(得分:0)

我发现vuex-router-sync是最简单的解决方案。在他们的网站上:

它将路线模块添加到商店中,其中包含代表当前路线的状态

答案 8 :(得分:0)

我使用 vuex-persistedstate 解决了这个问题。 这会将 vuex 状态保存在您的本地存储中。

在您的商店/index.js 中:

import createPersistedState from "vuex-persistedstate";

const store = new Vuex.Store({
  plugins: [createPersistedState()],
  state: {
     user: null,
     // ...
  },
  mutations: {// ...},
  actions: {// ...},
  getters: {// ...}
});