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数据类型转换-转换为数字型
开发语言·前端·javascript
—Qeyser2 小时前
Flutter StatelessWidget 完全指南:构建高效的静态界面
前端·flutter
AIGCExplore2 小时前
Jenkins 自动构建编译 Spring Boot 和 Vue 项目
vue.js·spring boot·jenkins
Tab6092 小时前
接入谷歌home/assistant/智能音箱
服务器·前端·智能音箱
倚栏听风雨2 小时前
深入浅出 TypeScript 模块系统:从语法到构建原理
前端
千里马-horse2 小时前
React Native bridging 源码分析--ClassTest.cpp
javascript·c++·react native·react.js·bridging
小高0072 小时前
2026 年,只会写 div 和 css 的前端将彻底失业
前端·javascript·vue.js
Anita_Sun2 小时前
Lodash 源码解读与原理分析 - Lodash 原型链的完整结构
前端
梁森的掘金2 小时前
Frida Hook 流程
前端