Vue页面布局与路由映射实战:RouterView嵌套及动态组件生成详解

一、布局组件的结构设计

1. 布局组件的核心作用

布局组件(如 AppProvider.vue

)是应用的顶层容器,负责:

1.1 统一头部、侧边栏、页脚等固定结构;

1.2 通过 渲染动态内容;

1.3 保持界面风格一致性。

1.4 共享国际化文件、统一处理elment组件样式等

1.5 和路由配置关联,可以定制多种布局方式

引用

css 复制代码
 export default defineComponent({
    name: 'AppProvider',
    inheritAttrs: false,
    props: {
      prefixCls: { type: String, default: prefixCls },
    },
    setup(_props) {
      // 动态改变页面title
      useTitle();

      // 监听屏幕变化
      createBreakpointListen();

      // 添加水印 - 嵌套模式下 不显示水印
      if (!isFrameMode()) useUserWatermark();

      // ElementPlus配置
      const { getElLocale } = useLocale();
      const elConf = computed(() => ({
        locale: getElLocale.value,
        message: { max: 2 },
        zIndex: 3000,
      }));

      return () => (
        <ElConfigProvider {...unref(elConf)}>
          <RouterView />
        </ElConfigProvider>
      );
    },
  });

路由文件示例

css 复制代码
import { LAYOUT } from '@/router/constant';
export default [
  {
    path: '/',
    component: LAYOUT,
    meta: { orderNo: 0, child1Hoist: true },
    redirect: PageEnum.BASE_HOME,
    children: [
      {
        path: PageEnum.BASE_HOME,
        name: 'Dashboard',
        meta: { title: '首页', icon: 'bi:house-door-fill', affix: true },
        component: () => import('@/views/dashboard/index.vue'),
      },
    ],
  },
  {
    path: '/',
    component: LAYOUT,
    meta: { hidden: true },
    redirect: '/user-center',
children: [
      {
        name: 'UserCenter',
        path: '/user-center',
        meta: { title: '个人信息', icon: 'el-icon-user', hidden: true },
        component: () => import('@/views/sys/user_center/index.vue'),
      },
    ],
  },
] as AppRouteModule[];

二、多层 RouterView 的嵌套布局

通过嵌套路由,布局组件可支持多层级内容渲染:

css 复制代码
<!-- Dashboard.vue (子路由组件) -->
<template>
  <div class="dashboard">
    <Sidebar />
    <div class="content">
      <!-- 渲染嵌套路由内容 -->
      <RouterView />
    </div>
  </div>
</template>

该文件的routerview是要动态渲染的内容区域,和子路由配置关联

三、静态组件 vs 动态组件

1. 静态组件(预定义路由)

定义:在路由配置文件中显式声明路径和组件的组件。

场景:固定功能页面(如登录页、404页)。

css 复制代码
// router/routes.ts
const routes = [
  {
    path: "/about",
    component: () => import("@/views/About.vue"), // 静态组件
  },
];
2. 动态组件(运行时生成)

定义:通过接口数据或业务逻辑动态生成路径和组件的组件。

场景:权限控制、菜单配置、微前端集成等。

引入组件

css 复制代码
// 动态引入组件
routes = asyncImportRoute(routeList);

路由处理

css 复制代码
export function asyncImportRoute(routes: AppRouteRecordRaw[], level = 1) {
  if (routes) {
    dynamicViewsModules = dynamicViewsModules || import.meta.glob('../../views/**/*.{vue,tsx}');

    routes.forEach((item) => {
      if (!item.component && item.meta?.frameSrc) item.component = 'IFRAME';

      const { component, children, name } = item;
      if (component) {
        const layoutFound = LayoutMap.get((component as string).toUpperCase());

       item.component = layoutFound || dynamicImport(dynamicViewsModules, component as string);
      } else {
        item.component = level === 1 ? LAYOUT : getParentLayout(name);
      }

      if (children && children.length) asyncImportRoute(children, level + 1);
    });
  }

  return routes;
}

dynamicImport

css 复制代码
/**
 * 根据组件名导入对应的视图
 *
 * @param dynamicViewsModules 一个记录,键是模块路径,值是返回Promise的函数,该Promise解析为一个记录。
 * @param component 需要动态导入的组件路径,可以是相对路径,且预期以'.vue'或'.tsx'结尾。
 * @returns 如果找到唯一的匹配项,则返回对应模块的加载函数。如果找到多个匹配项或无匹配项,将分别发出警告并返回undefined或EXCEPTION。
 */
function dynamicImport(
  dynamicViewsModules: Record<string, () => Promise<Recordable>>,
  component: string,
) {
 const keys = Object.keys(dynamicViewsModules);
  const matchKeys = keys.filter((key) => {
    const k = key.replace('../../views', '');
    const startFlag = component.startsWith('/');
    const endFlag = component.endsWith('.vue') || component.endsWith('.tsx');
    const startIndex = startFlag ? 0 : 1;
    const lastIndex = endFlag ? k.length : k.lastIndexOf('.');
    return k.substring(startIndex, lastIndex) === component;
  });
  if (matchKeys?.length === 1) {
    const matchKey = matchKeys[0];
    return dynamicViewsModules[matchKey];
  } else if (matchKeys?.length > 1) {
    $log.warn(
      '请不要在`src/views/`同级目录创建后缀为`.vue`或`.tsx`的同名文件, 这将会导致动态引入失败',
    );
    return;
  } else {
    $log.warn('在`src/views/`下找不到`' + component + '`, 请先在前端工程中创建该文件!');
    return EXCEPTION;
  }
}

核心要点:

  1. 对接口返回的动态路由最外层设置component属性值,设置布局组件
  2. 规范组件路由书写文件夹,比如统一在@/views下书写,配置路由的时候根据该目录去配置,动态加载子路由的component

四、 总结

  • 布局组件:通过 渲染动态内容,保持界面一致性。
  • 静态组件:路径固定,预定义在路由配置中。
  • 动态组件:通过接口或逻辑动态生成,无需预先写死路径。
  • 动态路由优势:支持权限控制、菜单扩展,提升代码灵活性与可维护性。
相关推荐
颜酱2 小时前
图结构完全解析:从基础概念到遍历实现
javascript·后端·算法
失忆爆表症3 小时前
05_UI 组件库集成指南:Shadcn/ui + Tailwind CSS v4
前端·css·ui
小迷糊的学习记录3 小时前
Vuex 与 pinia
前端·javascript·vue.js
发现一只大呆瓜3 小时前
前端性能优化:图片懒加载的三种手写方案
前端·javascript·面试
不爱吃糖的程序媛3 小时前
Flutter 与 OpenHarmony 通信:Flutter Channel 使用指南
前端·javascript·flutter
利刃大大3 小时前
【Vue】Element-Plus快速入门 && Form && Card && Table && Tree && Dialog && Menu
前端·javascript·vue.js·element-plus
NEXT063 小时前
AI 应用工程化实战:使用 LangChain.js 编排 DeepSeek 复杂工作流
前端·javascript·langchain
念风零壹4 小时前
AI 时代的前端技术:从系统编程到 JavaScript/TypeScript
前端·ai
光影少年4 小时前
react的hooks防抖和节流是怎样做的
前端·javascript·react.js
小毛驴8504 小时前
Vue 路由示例
前端·javascript·vue.js