Vue 라우터 모든 vue 컴포넌트 자동 등록
주소: /entry/vue-router-matches-every-view-component
문제
Vue 프로젝트에서 홈을 비롯해 여러 Vue 파일을 매칭시킨다.
import { createRouter, createWebHistory } from "vue-router";
import Home from "@/views/HomeView.vue";
import AAA from "@/views/AAAView.vue";
import BBB from "@/views/BBBView.vue";
import CAbc from "@/views/c/AbcView.vue";
import CDef from "@/views/c/DefView.vue";
const routes = [
{ name: "home", path: "/", component: Home },
{ name: "aaa", path: "/aaa", component: AAA },
{ name: "bbb", path: "/bbb", component: BBB },
{ name: "c-abc", path: "/c/abc", component: CAbc },
{ name: "c-def", path: "/c/def", component: CDef },
];
const router = createRouter({
history: createWebHistory(),
routes,
});
페이지 수가 많아질 수록 비슷한 코드가 많아진다.
풀이
그냥 /views 디렉토리 하위 Vue 파일들의 경로와 파일 이름을 이용해서 그 구조 그대로 등록해도 되지 않을까 생각이 든다.
import { createRouter, createWebHistory } from "vue-router";
/*
* 이름이 "View.vue"로 끝나는 모든 컴포넌트를 자동으로 라우트로 등록
*/
const vueFiles = import.meta.glob("@/views/**/*View.vue", { eager: true });
const routes = Object.keys(vueFiles).map((filePath) => {
const temp = filePath
.replace('/src/views/', '') // 상대경로로
.replace(/View\.vue$/, '') // "View.vue" 제거
.replace(/\/$/, '') // "/" 제거 (파일명이 View.vue 인 경우)
.toLowerCase();
const name = temp === '' ? 'home' : temp.replace(/\//g, '-');
const path = '/' + temp;
return {
name,
path,
component: (vueFiles as any)[filePath].default,
};
});
const router = createRouter({
history: createWebHistory(),
routes,
});
※ 이 경우 .../aaa/bbbView.vue 파일과 .../aaa/bbb/View.vue 파일이 있다면 중복이 된다.
없는 페이지
거기에 '없는 페이지' 페이지만 특별히 추가함.
import NotFound from '@/views/NotFound.vue';
const routes = Object.keys(vueFiles).map((filePath) => {
// ... 아까거
});
routes.push(// 없는 페이지 추가
{
path: '/:pathMatch(.*)*',
name: 'NotFound',
component: NotFound,
meta: {
requiresAuth: false
},
}
);
경로 파라미터
파일이름의 괄호를 경로 파라미터로 해석하도록 하여 경로 파라미터를 추가했다. 예: 파일 이름이 Some(id)View.vue이면 some/:id 경로로 매칭됨.
경로 문자열 처리에서 간단히 정규표현식 추가.
const routes = Object.keys(vueFiles).map((filePath) => {
const temp = filePath
.replace('/src/views/', '') // 상대경로로
.replace(/View\.vue$/, '') // 파일명 끝 "View.vue" 제거
.replace(/\(([^)]+)\)/g, "/:$1") // 괄호로 경로 파라미터 지정: '(param)' --> '/:param'
.replace(/\/$/, '') // "/" 제거 (파일명이 View.vue 인 경우)
.toLowerCase();
// ... 아까거 반복
});
현재 내 코드
import { createRouter, createWebHistory } from "vue-router";
import { useAuthStore } from "@/stores/auth";
import NotFound from '@/views/NotFound.vue';
//// 인증이 필요한 라우트 목록
const authRequiredRoutes = [
'/board/post/write',
'/member/myplace',
];
/*
* 이름이 "View.vue"로 끝나는 모든 컴포넌트를 자동으로 라우트로 등록
*/
const vueFiles = import.meta.glob("@/views/**/*View.vue", { eager: true });
const routes = Object.keys(vueFiles).map((filePath) => {
const temp = filePath
.replace('/src/views/', '') // 상대경로로
.replace(/View\.vue$/, '') // 파일명 끝 "View.vue" 제거
.replace(/\(([^)]+)\)/g, "/:$1") // 괄호로 경로 파라미터 지정: '(param)' --> '/:param'
.replace(/\/$/, '') // "/" 제거 (파일명이 View.vue 인 경우)
.toLowerCase();
const name = temp === '' ? 'home' : temp.replace(/\//g, '-');
const path = '/' + temp;
const requiresAuth = authRequiredRoutes.includes(path);
console.log(`★router - register: ${name} --> ${path}`);
return {
name,
path,
component: (vueFiles as any)[filePath].default,
meta: { requiresAuth }
};
});
routes.push(// 없는 페이지
{
path: '/:pathMatch(.*)*',
name: 'NotFound',
component: NotFound,
meta: {
requiresAuth: false
},
}
);
const router = createRouter({
history: createWebHistory(),
routes,
});
// 전역 가드
router.beforeEach((to, from) => {
// TEST
console.log(`★router: ${from.fullPath} --> ${to.fullPath}`);
const authStore = useAuthStore();
if (authStore.isLoggedIn) {// 로그인 상태
if (to.name === "memberLogin") {// 로그인페이지 --> 이전 페이지 or 홈
if (window.history.length > 1) {
return { path: from.fullPath };
} else {
return { name: "home" };// 이전 페이지 없음 --> 홈
}
}
} else {// 비로그인 상태
if (to.meta.requiresAuth) {// 로그인이 필요한 페이지
return { name: 'member-login', query: { redirect: to.fullPath } };
}
}
});
export default router;
참고
728x90