Vue 路由器 - 在一个导航守卫中多次调用“下一个”回调

问题描述 投票:0回答:1

我正在使用 Vue 3 和 Vue router 4。我创建了我的路由必须经过的中间件函数,但由于某种原因 Vue 抛出了这个错误:

当从“/”转到“/protected”时,“下一个”回调在一个导航防护中被多次调用。它应该在每个导航守卫中被调用一次。这将在生产中失败。

这是我的路线文件:

import auth from '@/middleware/auth'
import admin from '@/middleware/admin'
import initRoutesMiddleware from '@/routes/middleware'
import { createWebHistory, createRouter } from 'vue-router'

const routes = [
    {
        path: '/',
        name: 'dashboard',
        component: () => import('@/views/DashboardView.vue'),

        meta: {
            middleware: [ auth ],
        },
    },

    {
        path: '/protected',
        name: 'protected',
        component: () => import('@/views/ProtectedView.vue'),

        meta: {
            middleware: [ auth, admin ],
        },
    },

    {
        path: '/access-denied',
        name: 'accessDenied',
        component: () => import('@/views/error/AccessDeniedView.vue'),
    },
]

const router = createRouter({
    history: createWebHistory(),
    routes: routes,
})

router.beforeEach((_, __, next) => {
    setAppLoadingCursor(false)
    
    return next()
})

initRoutesMiddleware(router)

export default router

这是我的

middlware/auth.ts

import { ROUTE_LOGIN } from '@/config/routes'
import { LOCALSTORAGE_KEY_USER, LOCALSTORAGE_KEY_PATH_BEFORE_LOGIN } from '@/config/app'

export default function auth({ to, next }: any) {
    const user = !!localStorage.getItem(LOCALSTORAGE_KEY_USER)

    if (!user) {
        localStorage.setItem(LOCALSTORAGE_KEY_PATH_BEFORE_LOGIN, to.path)

        return next(ROUTE_LOGIN.path)
    }

    return next()
}

这是

middleware/admin.ts

import AppUser from '@/types/appUser'
import { LOCALSTORAGE_KEY_USER } from '@/config/app'
import { ROUTE_ACCESS_DENIED } from '@/config/routes'

export default function admin({ next }: any) {
    const user = localStorage.getItem(LOCALSTORAGE_KEY_USER)
    
    if (!user) return next()

    const appUser: AppUser = JSON.parse(user)

    if (appUser.admin !== true) return next(ROUTE_ACCESS_DENIED.path)

    return next()
}

这是

routes/middlware.ts

import { Router, RouteLocationNormalized, NavigationGuardNext } from 'vue-router'

interface RouteContext {
    from: RouteLocationNormalized
    next: NavigationGuardNext
    router: Router
    to: RouteLocationNormalized
}

function nextFactory(context: RouteContext, middlewares: Function[], index: number) {
    const subsequentMiddleware = middlewares[index]

    if (!subsequentMiddleware) return context.next

    return (...parameters: any[]) => {
        context.next(...(parameters as []))
        const nextMiddleware = nextFactory(context, middlewares, index + 1)
        subsequentMiddleware({ ...context, next: nextMiddleware })
    }
}

export default function initRoutesMiddleware(router: Router) {
    router.beforeEach((to, from, next) => {
        if (to.meta.middleware) {
            const middlewares: Function[] = Array.isArray(to.meta.middleware)
                ? to.meta.middleware
                : [to.meta.middleware]

            const context: RouteContext = { from, next, router, to }
            const nextMiddleware = nextFactory(context, middlewares, 1)

            return middlewares[0]({ ...context, next: nextMiddleware })
        }

        return next()
    })
}
javascript typescript vue.js vuejs3 vue-router
1个回答
1
投票

尝试以下操作(有关更多信息,请参阅下文):

routes/middleware.ts

import { Router, RouteLocationNormalized, NavigationGuard } from "vue-router";

type GuardFn = (
  to: RouteLocationNormalized,
  from: RouteLocationNormalized
) => ReturnType<NavigationGuard>;

export default function initRoutesMiddleware(router: Router) {
  router.beforeEach((to, from) => {
    if (to.meta.middleware) {
      const middlewares: GuardFn[] = Array.isArray(to.meta.middleware)
        ? to.meta.middleware
        : [to.meta.middleware];

      for (const middleware in middlewares) {
        const result = middlewares[middleware](to, from);
        if (result !== undefined && result !== true) return result;
      }
    }
  });
}

middleware/admin.ts

type AppUser = { admin: boolean };
const LOCALSTORAGE_KEY_USER = "";
const ROUTE_ACCESS_DENIED = { path: "" };

export default function admin() {
  const user = localStorage.getItem(LOCALSTORAGE_KEY_USER);

  if (!user) return;

  const appUser: AppUser = JSON.parse(user);

  if (appUser.admin !== true) return ROUTE_ACCESS_DENIED.path;
}

middleware/auth.ts

import { RouteLocationNormalized } from "vue-router";

const ROUTE_LOGIN = { path: "" };
const LOCALSTORAGE_KEY_USER = "";
const LOCALSTORAGE_KEY_PATH_BEFORE_LOGIN = "";

export default function auth(to: RouteLocationNormalized) {
  const user = !!localStorage.getItem(LOCALSTORAGE_KEY_USER);

  if (!user) {
    localStorage.setItem(LOCALSTORAGE_KEY_PATH_BEFORE_LOGIN, to.path);
    return ROUTE_LOGIN.path;
  }
}

您的代码显然多次

next
,考虑到所有递归过程的复杂性,这并不奇怪。
这实际上是错误的根源,因为这意味着每个中间件都试图执行自己的重定向或其他任何操作,这是您不希望发生的冲突。

我做了重大改变,如你所见:

我正在使用占位符类型和值,因为我显然无权访问您的完整项目。

© www.soinside.com 2019 - 2024. All rights reserved.