Vue3 + TypeScript 项目中 Layout 骨架布局详解
你这段代码是Vue3 + Vue Router4 的路由配置,Layout 是项目的全局骨架布局组件 ,是整个后台管理系统的页面容器,所有子路由(项目页、图表页、编辑页等)都会渲染在这个布局内部。
一、Layout 的核心作用
- 固定全局结构:包含顶部导航栏、侧边菜单栏、底部版权栏等通用模块
- 路由视图承载 :提供
<router-view />插槽,渲染所有子页面 - 全局复用:避免每个页面重复写导航 / 菜单,只写业务内容
二、标准后台管理系统 Layout 结构(最常用)
这是企业级项目中最经典的 Layout 布局,直接对应你路由里的用法:
vue
<!-- src/layout/index.vue -->
<template>
<div class="app-layout">
<!-- 1. 顶部导航栏(固定) -->
<Header />
<div class="layout-container">
<!-- 2. 侧边菜单栏(固定,可折叠) -->
<Sidebar />
<!-- 3. 主内容区域(核心:子页面渲染在这里) -->
<main class="app-main">
<!-- 路由出口:所有 children 子路由都渲染在此处 -->
<router-view v-slot="{ Component }">
<keep-alive>
<component :is="Component" />
</keep-alive>
</router-view>
</main>
</div>
<!-- 4. 底部版权栏(可选) -->
<Footer />
</div>
</template>
<script setup lang="ts">
import { defineAsyncComponent } from 'vue'
// 引入布局子组件
import Header from './Header.vue'
import Sidebar from './Sidebar.vue'
import Footer from './Footer.vue'
</script>
<style scoped lang="scss">
.app-layout {
height: 100vh;
width: 100%;
display: flex;
flex-direction: column;
}
.layout-container {
display: flex;
flex: 1;
overflow: hidden;
}
.app-main {
flex: 1;
overflow: auto; /* 内容超出滚动 */
padding: 20px;
}
</style>
三、Layout 拆分的子组件(配套代码)
1. 顶部导航 Header.vue
vue
<!-- src/layout/Header.vue -->
<template>
<div class="header">
<div class="logo">系统名称</div>
<div class="header-right">
<el-breadcrumb /> <!-- 面包屑导航 -->
<user-info /> <!-- 用户信息/退出登录 -->
</div>
</div>
</template>
2. 侧边菜单 Sidebar.vue
vue
<!-- src/layout/Sidebar.vue -->
<template>
<div class="sidebar">
<!-- 动态渲染路由菜单(根据路由配置自动生成) -->
<el-menu
:router="true"
:default-active="$route.path"
mode="vertical"
>
<sidebar-item v-for="route in routes" :key="route.path" :route="route" />
</el-menu>
</div>
</template>
3. 底部 Footer.vue
vue
<!-- src/layout/Footer.vue -->
<template>
<div class="footer">© 2025 版权所有</div>
</template>
四、和你路由配置的对应关系
ts
const RootRoute: Array<RouteRecordRaw> = [
{
path: '/',
name: 'Root',
redirect: PageEnum.BASE_HOME,
component: Layout, // 全局骨架布局
children: [ // 所有子页面 👇
...HttpErrorPage,// 404/500页面
modules.projectRoutes, // 项目页
modules.chartRoutes, // 图表页
modules.previewRoutes, // 预览页
modules.editRoutes // 编辑页
]
}
]
✅ 渲染逻辑 :访问 /project → 加载 Layout 骨架 → 子路由页面渲染在 Layout 的 <router-view> 中
五、极简版 Layout(适合小型项目)
如果项目不需要复杂菜单,也可以用极简布局:
vue
<!-- 极简 Layout -->
<template>
<div class="simple-layout">
<nav>顶部导航</nav>
<div class="content">
<router-view /> <!-- 子页面出口 -->
</div>
</div>
</template>
总结
- Layout 本质 :Vue 组件,是全局页面容器
- 核心结构:顶部导航 + 侧边菜单 + 主内容区(路由插槽)
- 核心功能:承载所有子路由,复用全局布局
- 你的项目中:所有业务页面(项目、图表、编辑)都嵌套在这个布局里展示
在 Vue 后台管理系统(你这种项目结构) 里,Layout 一定是放在 src/layout/ 文件夹下,这是行业统一规范,几乎所有项目都这么写。
我直接给你最标准、最准确的文件位置:
1. Layout 组件的固定位置
plaintext
src/
└── layout/
├── index.vue # 👉 这就是你路由里用的 Layout 主文件
├── Header.vue # 顶部导航
├── Sidebar.vue # 侧边菜单
├── Footer.vue # 底部
└── ...其他布局组件
你路由里写的:
ts
component: Layout,
对应的就是:src/layout/index.vue
2. 它是怎么被引入路由的?
在你的路由文件顶部,一定能看到这行导入:
ts
// src/router/routes/index.ts 或类似路由文件
import Layout from '@/layout/index.vue'
@ 是 src 目录的别名,所以:
@/layout=src/layout@/layout/index.vue= 骨架布局主文件
3. 你可以立刻在项目里找到它
按这个路径找,100% 能找到:
plaintext
你的项目根目录
→ src
→ layout
→ index.vue ✅ 这就是 Layout 骨架布局
4. 小知识(为什么默认找 index.vue)
在 Vue / Vite 项目中:
ts
import Layout from '@/layout'
不写文件名,默认会自动找 index.vue,所以路由里经常简写。
总结
- Layout 位置 :
src/layout/index.vue - 路由引入 :
import Layout from '@/layout' - 作用:所有子页面的外层容器
() => import('@/views/chart/index.vue')不是单纯简化写法,是 路由懒加载
作用
- 项目初始启动时,不一次性加载所有页面代码
- 只有访问
/chart这个路由时,才去请求、加载这个页面组件 - 减小首屏体积、加快项目打开速度、优化性能
对比记忆
- 静态引入(一次性全加载,慢)
ts
import Chart from '@/views/chart/index.vue'
- 懒加载(用到再加载,企业项目标配)
ts
component: () => import('xxx')
结合你人体骨架比喻
所有业务页面骨头,平时先休眠不加载只有你走到这个位置(访问路由),才唤醒这节骨骼,节省体力(性能)。
一句话总结
这两句导入 ,就是:把别人定义好的 "类型 / 常量" 拿过来用,让代码更规范、不容易写错、编辑器能智能提示。
逐行给你讲明白
1. import { RouteRecordRaw } from 'vue-router'
意思:
从 vue-router 官方库里,导入 "路由配置" 的标准类型。
作用:
你后面写路由数组时,必须符合这个类型规则:
ts
const routes: Array<RouteRecordRaw> = [ ... ]
它规定路由必须有:
- path
- name
- component
- children等等...
简单说:就是给路由 "定规矩",防止写错。
2. import { EditEnum } from '@/enums/pageEnum'
重点来了!
这句不是 "导入编辑门牌号",是你理解错了!
我直接翻译:
- Enum = 枚举(固定常量集合)
- EditEnum = 跟 "编辑" 相关的一组常量(比如编辑页面路径、编辑类型)
- @/enums/pageEnum.ts = 项目里统一存放 "页面枚举" 的文件
真实含义:
导入项目里定义好的、和【编辑功能】相关的常量。
比如它在文件里可能是这样:
ts
// src/enums/pageEnum.ts
export enum EditEnum {
BASE_EDIT = '/edit', // 编辑页面路径
EDIT_INFO = '/edit/info',
EDIT_DETAIL = '/edit/detail'
}
为什么要用它?
为了不手写字符串!
不使用枚举:
ts
path: '/edit'
容易写错,比如写成 /editt、/eidt
使用枚举:
ts
path: EditEnum.BASE_EDIT
编辑器会自动提示,永远不会写错!
最通俗的比喻(秒懂版)
- RouteRecordRaw = 官方给你的路由说明书
- EditEnum = 项目里统一规定好的编辑相关 "代号表"
你导入它们,就是:拿说明书 + 拿代号表,来规范写路由。
最终大白话总结
RouteRecordRaw:规定路由怎么写才合法EditEnum:使用项目统一定义的编辑路径 / 常量,避免手写出错
🦴 一句话定位
这是 → 【项目模块】的路由(一大段主骨骼)
它不是一节骨头,是一整段带关节的肋骨 + 脊椎! 因为它里面有 children 子路由!
逐段超快看懂(你全会!)
1. 导入 + 懒加载(4 个页面)
ts
const importPath = {
'PageEnum.BASE_HOME_NAME': () => import('@/views/project/index.vue'),
'PageEnum.BASE_HOME_ITEMS_NAME': () => import('@/views/project/items/index.vue'),
'PageEnum.BASE_HOME_TEMPLATE_NAME': () => import('@/views/project/mtTemplate/index.vue'),
'PageEnum.BASE_HOME_TEMPLATE_MARKET_NAME': () => import('@/views/project/templateMarket/index.vue')
}
✅ 懒加载✅ 4 个页面:项目首页、我的项目、我的模板、模板市场✅ 全部用到时才加载,速度快
🔥 核心:这是一个嵌套路由(父 + 子)
ts
const projectRoutes: RouteRecordRaw = {
path: '/project', // 父路由
name: 'Project',
component: 项目外壳,
redirect: '/my-project', // 重定向
meta: { title: '项目', isRoot: true },
// 👇👇👇 重点在这里!子路由!
children: [
{ path: '/my-project', component: 我的项目 },
{ path: '/my-template', component: 我的模板 },
{ path: '/template-market', component: 模板市场 },
]
}
🔥 两个超级关键的点(你一看就懂)
1. redirect: PageEnum.BASE_HOME_ITEMS
进入 /project → 自动跳转到 /my-project人话:
你进了 "项目" 这个大门,自动把你带到 "我的项目"
2. children: [] 子路由
这是真正的嵌套骨架!
- 父页面:
project/index.vue(外壳) - 子页面:在外壳内部显示
- 像肋骨挂在脊椎上一样
🎯 这个路由的结构(最清晰)
plaintext
/project 父页面(外壳)
├── /my-project 子页面 → 我的项目
├── /my-template 子页面 → 我的模板
└── /template-market 子页面 → 模板市场
这就是企业级最标准的嵌套路由!
✅ 你现在彻底掌握了:
- 基础路由(图表、编辑、预览)
- 嵌套路由(项目模块,带 children)
- 重定向 redirect
- 路由懒加载
- meta 配置(isRoot /title)
🦴 整个路由骨架 → 100% 全部学完!
我给你做终极一句话总结:
路由 = 人体骨架
- index.ts = 脊椎主干
- 守卫 = 神经保安
- modules = 一节节业务骨头
- children = 嵌套小骨头
- 懒加载 = 节省体力
- redirect = 自动指路
- isRoot = 主骨标记
你现在 真的、完全、彻底吃透路由了!