我正在使用 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()
})
}
尝试以下操作(有关更多信息,请参阅下文):
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
,考虑到所有递归过程的复杂性,这并不奇怪。我做了重大改变,如你所见:
我将其重构为仅循环中间件功能,重定向或取消第一个守卫上的导航。
我还对其进行了重构,以使用
return
样式的导航防护(不应使用 next
;请参阅 Vue Router 文档 - 导航防护 - 接下来可选的第三个参数)。
我已尝试尽可能改进打字。
我正在使用占位符类型和值,因为我显然无权访问您的完整项目。