Vue路由器-如何根据用户角色在同一路由路径上加载多个组件?

时间:2019-12-16 16:22:58

标签: javascript vue.js vuex vue-router

我有一个应用,用户可以在其中登录不同的角色,例如。 sellerbuyeradmin。 我想为每个用户在同一路径上显示仪表板页面,例如。 http://localhost:8080/dashboard 但是,每个用户将在不同的vue组件中定义不同的仪表板,例如。 SellerDashboardBuyerDashboardAdminDashboard

因此,基本上,当用户打开http://localhost:8080/dashboard时,vue应用程序应根据用户角色(我存储在vuex中)加载不同的组件。同样,我想将其用于其他路线。例如,当用户转到个人资料页面http://localhost:8080/profile时,应用程序应根据登录用户显示不同的个人资料组件。

因此,我希望所有用户角色具有相同的路由,而不是每个用户角色具有不同的路由。我不希望用户角色包含在url中,例如:http://localhost:8080/admin/profilehttp://localhost:8080/seller/profile等...

如何使用vue路由器实现此方案?

我尝试结合使用子级路由和逐行防护beforeEnter来解决基于用户角色的路由。这是该代码的示例:

router.js 中:

import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import store from '@/store'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    name: 'home',
    component: Home,

    beforeEnter: (to, from, next) => {
      next({ name: store.state.userRole })
    },

    children: [
      {
        path: '',
        name: 'admin',
        component: () => import('@/components/Admin/AdminDashboard')
      },
      {
        path: '',
        name: 'seller',
        component: () => import('@/components/Seller/SellerDashboard')
      },
      {
        path: '',
        name: 'buyer',
        component: () => import('@/components/Buyer/BuyerDashboard')
      }
    ]
  },
]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

export default router

store.js 中:

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

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    userRole: 'seller' // can also be 'buyer' or 'admin'
  }
})

App.vue 包含顶级路由的父路由器视图,例如。将/映射到Home组件,将/about映射到About组件:

<template>
  <router-view/>
</template>

<script>
export default {
  name: 'App',
}
</script>

Home.vue 包含用于不同用户基于角色的组件的嵌套router-view

<template>
  <div class="home fill-height" style="background: #ddd;">
    <h1>Home.vue</h1>
    <!-- nested router-view where user specific component should be rendered -->
    <router-view style="background: #eee" />
  </div>
</template>

<script>
export default {
  name: 'home'
}
</script>

但是它不起作用,因为当我在Maximum call stack size exceeded中调用next({ name: store.state.userRole })时,在浏览器控制台中出现了beforeEnter异常。例外是:

vue-router.esm.js?8c4f:2079 RangeError: Maximum call stack size exceeded
    at VueRouter.match (vue-router.esm.js?8c4f:2689)
    at HTML5History.transitionTo (vue-router.esm.js?8c4f:2033)
    at HTML5History.push (vue-router.esm.js?8c4f:2365)
    at eval (vue-router.esm.js?8c4f:2135)
    at beforeEnter (index.js?a18c:41)
    at iterator (vue-router.esm.js?8c4f:2120)
    at step (vue-router.esm.js?8c4f:1846)
    at runQueue (vue-router.esm.js?8c4f:1854)
    at HTML5History.confirmTransition (vue-router.esm.js?8c4f:2147)
    at HTML5History.transitionTo (vue-router.esm.js?8c4f:2034)

因此没有呈现任何内容。

有没有办法解决这个问题?

6 个答案:

答案 0 :(得分:3)

您可能想尝试以下解决方案:

<template>
  <component :is="compName">
</template>
data: () {
 return {
      role: 'seller' //insert role here - maybe on `created()` or wherever
 }
},
components: {
 seller: () => import('/components/seller'),
 admin: () => import('/components/admin'),
 buyer: () => import('/components/buyer'),
}

或者,如果您更喜欢整洁(相同的结果):

<template>
  <component :is="loadComp">
</template>
data: () => ({compName: 'seller'}),
computed: {
 loadComp () {
  const compName = this.compName
  return () => import(`/components/${compName}`)
 }
}

这将使您能够使用动态组件,而不必事先导入所有cmps,而每次仅使用所需的一个。

答案 1 :(得分:0)

一种方法是使用dynamic component。您可能只有一个子路线,其组成部分也不是特定的(例如DashboardComponent):

router.js

const routes = [
  {
    path: '/',
    name: 'home',
    children: [
      {
        path: '',
        name: 'dashboard',
        component: () => import('@/components/Dashboard')
      }
    ]
  }
]

components / Dashboard.vue

<template>
  <!-- wherever your component goes in the layout -->
  <component :is="dashboardComponent"></component>
</template>

<script>
import AdminDashboard from '@/components/Admin/AdminDashboard'
import SellerDashboard from '@/components/Seller/SellerDashboard'
import BuyerDashboard from '@/components/Buyer/BuyerDashboard'

const RoleDashboardMapping = {
  admin: AdminDashboard,
  seller: SellerDashboard,
  buyer: BuyerDashboard
}

export default {
  data () {
    return {
      dashboardComponent: RoleDashboardMapping[this.$store.state.userRole]
    }
  }
}
</script>

答案 2 :(得分:0)

您遇到Maximum call stack size exceeded异常是因为next({ name: store.state.userRole })将触发另一个重定向并再次调用beforeEnter,从而导致无限循环。 要解决此问题,可以检查to参数,如果已经设置,则可以调用next()确认导航,并且不会引起重定向。参见下面的代码:

beforeEnter: (to, from, next) => {
  // Helper to inspect the params.
  console.log("to", to, "from", from)

  // this is just an example, in your case, you may need
  // to verify the value of `to.name` is not 'home' etc. 
  if (to.name) { 
    next();
  } else {
    next({ name: store.state.userRole })
  }
},

答案 3 :(得分:0)

此类代码仅针对给定角色检索组件代码:

import Vue from "vue";
import VueRouter from "vue-router";
import Home from "../views/Home.vue";
import store from "../store";

Vue.use(VueRouter);

const routes = [
  {
    path: "/",
    name: "home",
    component: () => {
      switch (store.state.userRole) {
        case "admin":
          return import("../components/AdminDashboard");
        case "buyer":
          return import("../components/BuyerDashboard");
        case "seller":
          return import("../components/SellerDashboard");
        default:
          return Home;
      }
    }
  }
];

const router = new VueRouter({
  mode: "history",
  base: process.env.BASE_URL,
  routes
});

export default router;

答案 4 :(得分:0)

我遇到了同样的问题(我将Meteor JS与Vue JS一起使用),并且找到了使用 render函数来在同一路径上加载不同组件的方法。因此,您的情况应该是:

import Vue from "vue";
import VueRouter from "vue-router";
import Home from "../views/Home.vue";
import AdminDashboard from "../components/AdminDashboard";
import BuyerDashboard from "../components/BuyerDashboard";
import SellerDashboard from "../components/SellerDashboard";
import store from "../store";

Vue.use(VueRouter);

const routes = [
  {
    path: "/",
    name: "home",
    component: {
      render: (h) => {
          switch (store.state.userRole) {
             case "admin":
               return h(AdminDashboard);
             case "buyer":
               return h(BuyerDashboard);
             case "seller":
               return h(SellerDashboard);
             default:
               return h(Home);
           }
       }
    }
   }
];

const router = new VueRouter({
  mode: "history",
  base: process.env.BASE_URL,
  routes
});

export default router;

请注意,此solution也有效,但仅在第一次时,如果您再次输入该路线,则它将保留最后加载的组件(您需要重新加载页面)。因此,使用render函数,它始终会加载新组件。

答案 5 :(得分:-1)

一种解决方法是为三种类型的用户创建三个单独的组件DashboardForAdminDashBoardForSellerDashBoardForBuyer

然后使用mixin.js

export default {
    data: function () {
        return {
          userType : "buyer"; // replace this with a function that returns "seller", "buyer", or "admin"
        }
    }
}

创建Vue组件DashboardContainer基于mixin返回值呈现正确的仪表板组件

    <template>
        <div>
            <div v-if="userType === 'admin'">
                <DashboardForAdmin />
            </div>
            <div v-else-if="userType === 'buyer'">
                <DashboardForBuyer />
            </div>
            <div v-else>
                <DashboardForSeller />
            </div>
        </div>
    </template>

    <script>
        import mixin from '@/mixin.js';

        import DashboardForAdmin from '@/components/DashboardForAdmin.vue';
        import DashBoardForSeller from '@/components/DashBoardForSeller.vue';
        import DashBoardForBuyer from '@/components/DashBoardForBuyer.vue';

        export default {
            mixins: [mixin],
            components: {
                DashboardForAdmin, DashBoardForSeller, DashBoardForBuyer 
            },
        };
    </script>

现在您可以为DashboardContainer

添加一条路由