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

四、 总结

  • 布局组件:通过 渲染动态内容,保持界面一致性。
  • 静态组件:路径固定,预定义在路由配置中。
  • 动态组件:通过接口或逻辑动态生成,无需预先写死路径。
  • 动态路由优势:支持权限控制、菜单扩展,提升代码灵活性与可维护性。
相关推荐
weixin_382395237 小时前
为小工厂量身打造:本地部署的物料管理系统带缺料计算
前端·制造
__zRainy__7 小时前
解决pnpm v10+不自动构建
前端·pnpm·工程化
猫猫不是喵喵.8 小时前
Vue3 Props 属性
前端·javascript·vue.js
醉城夜风~9 小时前
CSS元素显示模式(display)
前端·css
AI大模型-小华9 小时前
Codex 三方充值快速入门指南
java·前端·数据库·chatgpt·ai编程·codex·chatgpt pro
做前端的娜娜子12 小时前
同一链接实现 PC Web 与移动 H5 自适应
前端·掘金·金石计划
小帅不太帅12 小时前
架构没变、规模没变,DeepSeek V4 Flash 正式版凭什么暴涨 47 分?
前端·aigc·deepseek
jarvisuni12 小时前
DeepSeekFlash前端依旧拉垮,而且变慢了很多!
前端·javascript·算法
卷福同学13 小时前
AI编程出海第二步:验证关键词能否做站
前端·人工智能·后端