Vue router-как иметь несколько компонентов, загруженных на одном пути маршрута на основе роли пользователя?

Vue router-как иметь несколько компонентов, загруженных на одном пути маршрута на основе роли пользователя?

16.12.2019 07:22:58 Просмотров 92 Источник

У меня есть приложение, где пользователь может войти в систему в разных ролях, например. seller, buyerи admin. Для каждого пользователя я хотел бы показать страницу dashboard по одному и тому же пути, например. http://localhost:8080/dashboard Однако у каждого пользователя будет своя панель мониторинга, определенная в разных компонентах vue, например. SellerDashboard, BuyerDashboardи AdminDashboard.

Так что в основном, когда пользователь открывает http://localhost:8080/dashboardприложение vue должно загружать разные компоненты в зависимости от роли пользователя (которую я храню в vuex). Точно так же я хотел бы иметь это для других маршрутов. Например, когда пользователь переходит на страницу http://localhost:8080/profilehttp://localhost:8080/profile приложение должно показывать разные компоненты профиля в зависимости от вошедшего пользователя.

Поэтому я хотел бы иметь один и тот же маршрут для всех ролей пользователей, а не иметь разные маршруты для каждой роли пользователя, например. Я не хочу, чтобы роль пользователя должна содержаться в URL, как следующие: http://localhost:8080/admin/profile и http://localhost:8080/seller/profile и т. д...

Как я могу реализовать этот сценарий с маршрутизатором vue?

Я попытался использовать комбинацию дочерних маршрутов и per-route guard beforeEnterдля разрешения маршрута на основе роли пользователя. Вот пример кода для этого:

в роутере.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

в магазине.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'
  }
})

Апп.vue содержит Родительский маршрутизатор-представление для маршрутов верхнего уровня, например. карта / для Home компонент и /about С About компонент:

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

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

И Домой.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)

и таким образом ничего не передается.

Есть ли способ решить эту проблему?

У вопроса есть решение - Посмотреть?

Ответы - Vue router-как иметь несколько компонентов, загруженных на одном пути маршрута на основе роли пользователя? / Vue router - how to have multiple components loaded on the same route path based on user role?

Matt U

16.12.2019 07:38:39

Один из подходов заключается в использовании динамического компонента . У вас может быть один дочерний маршрут, компонент которого также не является специфичным (напримерDashboardComponent):

обращать в бегство.JS

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

компоненты / приборная панель.вю

<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>
https://stackoverflow.com/questions/59360512/vue-router-how-to-have-multiple-components-loaded-on-the-same-route-path-based/59360776#comment104916323_59360776
Спасибо за ответ, но это может быть не то, что мне нужно. В идеале я хотел бы использовать webpack import()для динамического извлечения только тех фрагментов приложения, которые требуются для текущей роли пользователя. Напр.. если пользователь вошел в систему как администратор, я не хочу загружать компоненты для других ролей. В вашем случае я бы статически импортировал все компоненты в Dashboard.vue и таким образом доставить их всем пользователям независимо от роли. Во-вторых, я бы хотел, чтобы эта логика содержалась в маршрутизаторе, а не распространялась на несколько универсальных компонентов, которые используются только в качестве контейнеров для их ролевых версий.
https://stackoverflow.com/questions/59360512/vue-router-how-to-have-multiple-components-loaded-on-the-same-route-path-based/59360776#comment104916389_59360776
Как насчет одного дочернего маршрута и nameдля store.state.userRole, а затем динамически построить путь importна основе этого значения, а также?
https://stackoverflow.com/questions/59360512/vue-router-how-to-have-multiple-components-loaded-on-the-same-route-path-based/59360776#comment104916895_59360776
Динамическое построение import()не будет работать, потому import()работает только со статическими строками, определенными заранее во время сборки. Это необходимо для того, чтобы webpack знал, какие файлы js должны быть в комплекте и т. д... Но я попробовал это на всякий случай сейчас и действительно подтверждаю это, поскольку я получаю исключение времени выполнения, когда я пытаюсь загрузить компонент маршрута динамически vue-router.esm.js?8c4f:2079 Error: Cannot find module '@/components/Seller/SellerProfile' :(
https://stackoverflow.com/questions/59360512/vue-router-how-to-have-multiple-components-loaded-on-the-same-route-path-based/59360776#comment104917114_59360776
А, попался. К сожалению, мой ответ-это не то, что вы искали, это была просто моя первая мысль. Я относительно новичок в Vue (но у меня есть некоторый опыт в других front end фреймворках).
https://stackoverflow.com/questions/59360512/vue-router-how-to-have-multiple-components-loaded-on-the-same-route-path-based/59360776#comment104917132_59360776
Не беспокойтесь, спасибо за усилия :)
https://stackoverflow.com/questions/59360512/vue-router-how-to-have-multiple-components-loaded-on-the-same-route-path-based/59360776#comment104919973_59360776
@mlst из любопытства (я не могу проверить это в данный момент) что, если вы добавляете регистрацию перед beforeEnterнапример, if (to.name !== store.state.userRole) { next({ name: store.state.userRole }) } ... а потом else { next() }?
Jesus

19.12.2019 01:46:17

Возможно, вы захотите попробовать что-то вокруг этого решения:

<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 заранее, но используя только один необходимый каждый раз.

lenglei

19.12.2019 10:45:09

Вы сталкиваетесь с Maximum call stack size exceededисключение, потому next({ name: store.state.userRole })вызовет еще одно перенаправление и beforeEnterbeforeEnter, что приведет к бесконечному циклу. Чтобы решить эту проблему, вы можете проверить параметр toparam, и если он уже установлен, вы можете вызвать 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 })
  }
},
sean717

19.12.2019 11:07:40

Одним из способов решения этой проблемы является создание трех отдельных компонентов DashboardForAdmin, DashBoardForSellerи DashBoardForBuyerдля трех типов пользователей.

Затем используйте миксин.JS

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

Создание компонента DashboardContainerDashboardContainer отображает правильный компонент панели мониторинга на основе возвращаемого значения 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

Husam Ibrahim

23.12.2019 12:57:50

Вы получаете этот Maximum call stack size exceededисключение, потому что вы создаете бесконечный цикл, где вы повторно перенаправляетесь на один и тот же маршрут и вводите один и тот же beforeEnterгде-то в этом крючке, чтобы подтвердить нормальную навигацию, а не перенаправлять ее. Например..

next()

Я думаю, что вы могли бы также использовать историю.replaceState () API (который является тем, что маршрутизатор использует внутренне для обновления отображаемого URL-адреса), чтобы программно обновить URL-адрес, чтобы отразить то, что вы хотите. В этом случае у вас будет обычная конфигурация маршрутизатора vue с записью пути для каждой роли, но вы будете использовать beforeEnter: (to, from, next) => { if (from.path == '/') { next() } else { next({ name: store.state.userRole }) } }, для отображения History.replaceState()в URL-адресе для всех ролей пользователей.

Gander

25.12.2019 04:45:08

Такой код извлекает код компонента только для данной роли:

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;
Закрыть X