普通视图

发现新文章,点击刷新页面。
今天 — 2026年3月7日首页

Vue生态精选篇:Element Plus 的“企业后台常用组件”用法扫盲

作者 SuperEugene
2026年3月7日 12:53

同学们好,我是 Eugene(尤金),一个拥有多年中后台开发经验的前端工程师~

(Eugene 发音很简单,/juːˈdʒiːn/,大家怎么顺口怎么叫就好)

你是否也有过:明明学过很多技术,一到关键时候却讲不出来、甚至写不出来?

你是否也曾怀疑自己,是不是太笨了,明明感觉会,却总差一口气?

就算想沉下心从头梳理,可工作那么忙,回家还要陪伴家人。

一天只有24小时,时间永远不够用,常常感到力不从心。

技术行业,本就是逆水行舟,不进则退。

如果你也有同样的困扰,别慌。

从现在开始,跟着我一起心态归零,利用碎片时间,来一次彻彻底底的基础扫盲

这一次,我们一起慢慢来,扎扎实实变强。

不搞花里胡哨的理论堆砌,只分享看得懂、用得上的前端干货,

咱们一起稳步积累,真正摆脱“面向搜索引擎写代码”的尴尬。

一、选型与定位

  • Element Plus:面向 Vue 3 + TypeScript 的 UI 组件库,适合管理后台、中台、后台系统。
  • 为什么用组件库而不是手写? 统一规范、减少重复开发、内置表单校验、表格、弹窗等常见能力。
  • 本文涉及组件:Form、Table、Dialog、Message/MessageBox、Upload。

二、表单 Form:数据收集与校验

2.1 核心概念

Form 的作用:收集、校验、提交 数据,包含输入框、选择器、日期等。

表单的三层结构:

  1. el-form:表单容器,绑定数据和校验规则
  2. el-form-item:单个表单项,承载 label、校验、布局
  3. el-input / el-select 等:具体输入控件

2.2 正确用法示例

<template>
  <el-form 
    ref="formRef" 
    :model="form" 
    :rules="rules" 
    label-width="100px"
    @submit.prevent
  >
    <el-form-item label="用户名" prop="username">
      <el-input v-model="form.username" placeholder="请输入用户名" />
    </el-form-item>
    
    <el-form-item label="密码" prop="password">
      <el-input v-model="form.password" type="password" placeholder="请输入密码" />
    </el-form-item>
    
    <el-form-item>
      <el-button type="primary" @click="handleSubmit">提交</el-button>
      <el-button @click="handleReset">重置</el-button>
    </el-form-item>
  </el-form>
</template>

<script setup>
import { ref, reactive } from 'vue'

const formRef = ref()
const form = reactive({
  username: '',
  password: ''
})

// 校验规则:字段名要与 form 中的属性、el-form-item 的 prop 完全一致
const rules = {
  username: [
    { required: true, message: '请输入用户名', trigger: 'blur' },
    { min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
  ],
  password: [
    { required: true, message: '请输入密码', trigger: 'blur' },
    { min: 6, message: '密码至少 6 位', trigger: 'blur' }
  ]
}

const handleSubmit = async () => {
  // validate 返回 Promise,通过则无参数,失败则返回校验错误
  try {
    await formRef.value.validate()
    console.log('校验通过,提交数据:', form)
    // 这里调用接口提交
  } catch (error) {
    console.log('校验失败')
  }
}

const handleReset = () => {
  formRef.value.resetFields()
}
</script>

说明要点:

  • :model="form" 绑定表单数据,注意是 :model,不是 v-model
  • :rules="rules" 绑定校验规则
  • prop="username" 绑定到表单项,用于关联 rules 中的字段
  • @submit.prevent 防止回车键意外提交表单

2.3 常见踩坑

错误写法 正确写法
Form 绑定 v-model="form" :model="form"
不写 prop <el-form-item> 无 prop <el-form-item prop="username">
prop 写错位置 写在 el-input 必须写在 el-form-item
prop 与 rules 不一致 rules 里是 name,prop 是 username 两者字段名完全一致

记住:el-form 用 :model、el-form-item 必须有 prop、prop 与 rules 字段名一致

2.4 常用 API

  • validate():整表校验
  • validateField(prop):校验单个字段
  • resetFields():重置表单
  • clearValidate():清除校验状态

三、表格 Table:列表展示

3.1 核心概念

Table 用于展示列表数据,支持排序、分页、选择、展开等。

3.2 基础用法示例

<template>
  <el-table 
    :data="tableData" 
    stripe 
    border
    style="width: 100%"
    @selection-change="handleSelectionChange"
  >
    <!-- 多选列 -->
    <el-table-column type="selection" width="55" />
    
    <!-- 普通列 -->
    <el-table-column prop="name" label="姓名" width="120" />
    <el-table-column prop="age" label="年龄" width="80" />
    <el-table-column prop="address" label="地址" show-overflow-tooltip />
    
    <!-- 自定义列 -->
    <el-table-column label="状态" width="100">
      <template #default="{ row }">
        <el-tag :type="row.status === 1 ? 'success' : 'info'">
          {{ row.status === 1 ? '启用' : '禁用' }}
        </el-tag>
      </template>
    </el-table-column>
    
    <!-- 操作列 -->
    <el-table-column label="操作" width="180" fixed="right">
      <template #default="{ row }">
        <el-button link type="primary" @click="handleEdit(row)">编辑</el-button>
        <el-button link type="danger" @click="handleDelete(row)">删除</el-button>
      </template>
    </el-table-column>
  </el-table>
</template>

<script setup>
import { ref } from 'vue'

const tableData = ref([
  { id: 1, name: '张三', age: 28, address: '上海市浦东新区某某路100号', status: 1 },
  { id: 2, name: '李四', age: 32, address: '北京市朝阳区某某大街200号', status: 0 }
])

const handleSelectionChange = (selection) => {
  console.log('选中的行:', selection)
}

const handleEdit = (row) => {
  console.log('编辑', row)
}

const handleDelete = (row) => {
  console.log('删除', row)
}
</script>

说明要点:

  • :data 绑定数据数组,每一行是一个对象
  • prop 对应数据字段名,决定显示哪个字段
  • show-overflow-tooltip:内容过长时显示省略号并悬浮显示完整内容
  • #default="{ row }":插槽提供当前行数据

3.3 配置选型建议

场景 推荐配置
数据较多 heightmax-height 固定高度,出现纵向滚动
树形数据 使用 row-key + tree-props
需要合计 show-summary + summary-method
列宽不稳定 设置 widthmin-width,避免抖动
多选 type="selection" + @selection-change

3.4 常见踩坑

  • 表格数据不更新:确保 tableData 是响应式的(如 ref),修改后要触发更新
  • 树形表格:必须设置 row-key 为唯一字段(如 id
  • 固定列fixed="right"fixed="left" 时,注意右侧固定列写在最后

四、弹窗 Dialog:模态对话框

4.1 核心概念

Dialog 用于在保留当前页面的前提下,弹出一个模态层展示内容,常用于表单弹窗、详情、确认等。

4.2 基础用法示例

<template>
  <el-button @click="dialogVisible = true">打开弹窗</el-button>
  
  <el-dialog
    v-model="dialogVisible"
    title="编辑用户"
    width="500px"
    :close-on-click-modal="false"
    :before-close="handleBeforeClose"
    @opened="handleOpened"
  >
    <!-- 弹窗内容 -->
    <el-form ref="formRef" :model="form" :rules="rules">
      <el-form-item label="用户名" prop="username">
        <el-input v-model="form.username" />
      </el-form-item>
    </el-form>
    
    <template #footer>
      <span class="dialog-footer">
        <el-button @click="dialogVisible = false">取消</el-button>
        <el-button type="primary" @click="handleConfirm">确定</el-button>
      </template>
    </template>
  </el-dialog>
</template>

<script setup>
import { ref, reactive, watch } from 'vue'

const dialogVisible = ref(false)
const formRef = ref()
const form = reactive({ username: '' })
const rules = { username: [{ required: true, message: '请输入用户名', trigger: 'blur' }] }

// 弹窗关闭前:可做二次确认、校验等
const handleBeforeClose = (done) => {
  // 简单示例:直接关闭
  done()
  // 如需确认:ElMessageBox.confirm('确定关闭?').then(() => done()).catch(() => {})
}

// 弹窗打开动画结束后
const handleOpened = () => {
  formRef.value?.clearValidate()
}

// 关闭时清空表单(按需)
watch(dialogVisible, (val) => {
  if (!val) {
    form.username = ''
  }
})

const handleConfirm = async () => {
  try {
    await formRef.value.validate()
    // 提交逻辑
    dialogVisible.value = false
  } catch (e) {
    // 校验失败
  }
}
</script>

说明要点:

  • v-model="dialogVisible" 控制显示/隐藏
  • :close-on-click-modal="false":点击遮罩不关闭,避免误关
  • before-close:可做二次确认、阻止关闭
  • #footer:自定义底部按钮

4.3 常见配置选型

配置 说明 建议
destroy-on-close 关闭时销毁内容 表单弹窗建议开启,避免数据残留
close-on-click-modal 点击遮罩关闭 表单弹窗建议关闭
append-to-body 挂载到 body 有嵌套弹窗时建议开启

五、消息 Message 与 MessageBox

5.1 ElMessage:轻量提示

用于操作后的简单反馈(成功、失败、警告等),通常显示几秒后自动消失。

import { ElMessage } from 'element-plus'

// 成功
ElMessage.success('保存成功')

// 错误
ElMessage.error('保存失败,请重试')

// 警告
ElMessage.warning('请先填写必填项')

// 自定义
ElMessage({
  message: '操作成功',
  type: 'success',
  duration: 3000,
  showClose: true
})

5.2 ElMessageBox:确认与输入

用于需要用户确认或输入的场景,比 Dialog 更轻量。

import { ElMessageBox } from 'element-plus'

// 确认删除
const handleDelete = async (row) => {
  try {
    await ElMessageBox.confirm(
      `确定要删除「${row.name}」吗?`,
      '提示',
      {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }
    )
    // 用户点击确定
    await deleteApi(row.id)
    ElMessage.success('删除成功')
  } catch (e) {
    // 用户点击取消或关闭
  }
}

// 简单提示(类似 alert)
ElMessageBox.alert('操作完成', '提示')

5.3 选型建议

场景 用 Message 用 MessageBox
保存成功、失败提示
删除前确认
需要用户输入 ✅(prompt)
复杂表单、多内容 改用 Dialog

六、上传 Upload:文件上传

6.1 核心概念

Upload 支持自动上传和手动上传:自动上传是选完即传,手动上传是选完后由按钮触发上传。

6.2 自动上传(选完即传)

<template>
  <el-upload
    action="/api/upload"
    :headers="uploadHeaders"
    :on-success="handleSuccess"
    :on-error="handleError"
    :before-upload="beforeUpload"
  >
    <el-button type="primary">点击上传</el-button>
  </el-upload>
</template>

<script setup>
import { reactive } from 'vue'

// 请求头,常用于 Token
const uploadHeaders = reactive({
  Authorization: `Bearer ${localStorage.getItem('token')}`
})

// 上传前:校验格式、大小
const beforeUpload = (file) => {
  const isJPG = file.type === 'image/jpeg' || file.type === 'image/png'
  const isLt2M = file.size / 1024 / 1024 < 2

  if (!isJPG) {
    ElMessage.error('只能上传 JPG/PNG 格式')
    return false  // 阻止上传
  }
  if (!isLt2M) {
    ElMessage.error('图片大小不能超过 2MB')
    return false
  }
  return true
}

const handleSuccess = (response, file, fileList) => {
  ElMessage.success('上传成功')
  // response 一般为后端返回的 URL 等
}

const handleError = () => {
  ElMessage.error('上传失败')
}
</script>

6.3 手动上传(和表单一起提交)

<template>
  <el-form :model="form">
    <el-form-item label="附件">
      <el-upload
        ref="uploadRef"
        :auto-upload="false"
        :limit="3"
        :on-exceed="handleExceed"
        :on-change="handleChange"
      >
        <el-button type="primary">选择文件</el-button>
      </el-upload>
    </el-form-item>
    <el-button @click="submitForm">提交表单(含文件)</el-button>
  </el-form>
</template>

<script setup>
import { ref } from 'vue'

const uploadRef = ref()
const form = ref({ files: [] })

// 手动上传时,选中的文件会进入 fileList,需要自己调用接口上传
const handleChange = (file, fileList) => {
  form.value.files = fileList
}

const handleExceed = () => {
  ElMessage.warning('最多上传 3 个文件')
}

const submitForm = async () => {
  const formData = new FormData()
  form.value.files.forEach(f => {
    formData.append('files', f.raw)
  })
  // 再 append 其他表单字段...
  // await uploadApi(formData)
}
</script>

说明要点:

  • :auto-upload="false" 关闭自动上传
  • on-change 拿到选中的文件列表
  • 手动上传时用 FormData 组装并调用自己的接口

6.4 常见踩坑

原因 处理
before-upload 返回 false 仍上传 理解错误 返回 falsePromise.reject() 会阻止上传
上传后列表不更新 未绑定 file-list v-model:file-list:file-list 绑定
跨域、Cookie 未带凭证 设置 :with-credentials="true"
需要 Token 接口要鉴权 通过 :headers 传入

七、小结

  • Form:用 :model + prop + rules,三者字段名一致
  • Tableprop 对数据字段,复杂展示用 #default 插槽
  • Dialog:用 v-model 控制显隐,表单弹窗建议 destroy-on-close
  • Message:轻量提示;MessageBox:确认、输入
  • Upload:自动上传用 action + 钩子;手动上传用 :auto-upload="false" + 自定义提交

按上述方式选型和编码,可以避开大部分常见坑。如果你希望我按某一块(比如 Form、Table、Upload)再单独细化成一篇更长的教程,可以说明一下侧重点(例如:复杂表单、动态表格、多图上传等)。


学习本就是一场持久战,不需要急着一口吃成胖子。哪怕今天你只记住了一点点,这都是实打实的进步。

后续我还会继续用这种大白话、讲实战方式,带大家扫盲更多前端基础。

关注我,不迷路,咱们把那些曾经模糊的知识点,一个个彻底搞清楚。

如果你觉得这篇内容对你有帮助,不妨点赞+收藏,下次写代码卡壳时,拿出来翻一翻,比搜引擎更靠谱。

我是 Eugene,你的电子学友,我们下一篇干货见~

昨天 — 2026年3月6日首页

路由与布局骨架篇:布局系统 | 头部、侧边栏、内容区、面包屑的拆分与复用

作者 SuperEugene
2026年3月6日 09:19

同学们好,我是 Eugene(尤金),一个拥有多年中后台开发经验的前端工程师~

(Eugene 发音很简单,/juːˈdʒiːn/,大家怎么顺口怎么叫就好)

你是否也有过:明明学过很多技术,一到关键时候却讲不出来、甚至写不出来?

你是否也曾怀疑自己,是不是太笨了,明明感觉会,却总差一口气?

就算想沉下心从头梳理,可工作那么忙,回家还要陪伴家人。

一天只有24小时,时间永远不够用,常常感到力不从心。

技术行业,本就是逆水行舟,不进则退。

如果你也有同样的困扰,别慌。

从现在开始,跟着我一起心态归零,利用碎片时间,来一次彻彻底底的基础扫盲

这一次,我们一起慢慢来,扎扎实实变强。

不搞花里胡哨的理论堆砌,只分享看得懂、用得上的前端干货,

咱们一起稳步积累,真正摆脱“面向搜索引擎写代码”的尴尬。

一、先搞清楚一件事:什么是布局?

布局(Layout)就是页面里不随路由变的那一部分:头部、侧边栏、面包屑、底部等。
真正随路由变化的是「内容区」。布局负责把这些固定区域包起来,内容区填进其中。

  • 布局:结构固定、多页面共用
  • 内容区:随路由切换、每页不同

理解了这一点,再去看 Vue Router 的嵌套路由,就很好理解。

二、为什么要拆分布局组件?

不拆的话,每个页面都要写一遍头部、侧边栏,会有这些问题:

  1. 重复代码多
  2. 改头部要改 N 个页面
  3. 页面结构和布局混在一起,难维护

拆分后:

  • 布局组件:只负责头部、侧边栏等固定结构
  • 内容区:只负责当前页面的业务
  • 路由:负责决定「用哪个布局」「在哪个槽位渲染内容」

三、整体结构预览

Layout(布局容器)
├── AppHeader(头部)
├── AppSidebar(侧边栏)
├── Breadcrumb(面包屑,可选)
└── 内容区(由 <router-view> 渲染)

接下来按「路由配置 → 布局组件 → 各子组件」的顺序说明。

四、路由配置:布局与路由如何配合?

核心思路:用嵌套路由,父路由用 Layout,子路由占内容区。

4.1 基础路由结构

// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import Layout from '@/layouts/BasicLayout.vue'

const routes = [
  {
    path: '/',
    component: Layout,  // 父路由使用布局组件
    redirect: '/dashboard',
    children: [
      {
        path: 'dashboard',
        name: 'Dashboard',
        component: () => import('@/views/Dashboard.vue'),
        meta: { title: '仪表盘', icon: 'dashboard' }
      },
      {
        path: 'user',
        name: 'User',
        component: () => import('@/views/User.vue'),
        meta: { title: '用户管理', icon: 'user' }
      }
    ]
  }
]

const router = createRouter({
  history: createWebHistory(),
  routes
})

export default router

要点:

  • 父路由 path: '/'Layout
  • children 里的每个路由才是具体页面
  • meta 用来存标题、图标等,后面给面包屑和菜单用

4.2 多个布局怎么办?

例如:后台用带侧边栏的布局,登录页用简单布局。

const routes = [
  // 后台布局(带侧边栏)
  {
    path: '/',
    component: () => import('@/layouts/BasicLayout.vue'),
    children: [
      { path: 'dashboard', component: () => import('@/views/Dashboard.vue'), meta: { title: '仪表盘' } },
      { path: 'user', component: () => import('@/views/User.vue'), meta: { title: '用户管理' } }
    ]
  },
  // 登录页布局(无侧边栏)
  {
    path: '/login',
    component: () => import('@/layouts/BlankLayout.vue'),
    children: [
      { path: '', component: () => import('@/views/Login.vue') }
    ]
  }
]

每个布局对应一个父路由,它的 children 共用同一个布局。

五、布局组件 BasicLayout.vue

5.1 完整示例

<!-- layouts/BasicLayout.vue -->
<template>
  <el-container class="basic-layout">
    <!-- 头部 -->
    <AppHeader />
    
    <el-container>
      <!-- 侧边栏 -->
      <AppSidebar />
      
      <!-- 主内容区 -->
      <el-main class="main-content">
        <!-- 面包屑 -->
        <Breadcrumb />
        <!-- 内容区:由路由渲染 -->
        <div class="content-wrapper">
          <router-view v-slot="{ Component }">
            <transition name="fade" mode="out-in">
              <component :is="Component" />
            </transition>
          </router-view>
        </div>
      </el-main>
    </el-container>
  </el-container>
</template>

<script setup>
import AppHeader from './components/AppHeader.vue'
import AppSidebar from './components/AppSidebar.vue'
import Breadcrumb from './components/Breadcrumb.vue'
</script>

<style scoped>
.basic-layout {
  min-height: 100vh;
  flex-direction: column;
}
.main-content {
  padding: 20px;
  background: #f5f7fa;
}
.content-wrapper {
  margin-top: 16px;
  padding: 20px;
  background: #fff;
  border-radius: 4px;
  min-height: calc(100vh - 180px);
}
.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.2s ease;
}
.fade-enter-from,
.fade-leave-to {
  opacity: 0;
}
</style>

要点:

  • <router-view> 就是子路由渲染的地方
  • v-slot="{ Component }" + <component :is="Component"> 可以配合过渡动画
  • 没有用 Element Plus 的话,把 el-container 换成普通 div 即可

六、各子组件实现

6.1 头部 AppHeader.vue

<!-- layouts/components/AppHeader.vue -->
<template>
  <header class="app-header">
    <div class="header-left">
      <span class="logo">后台管理系统</span>
    </div>
    <div class="header-right">
      <span class="user-name">管理员</span>
      <button @click="handleLogout">退出</button>
    </div>
  </header>
</template>

<script setup>
const handleLogout = () => {
  // 登出逻辑
  console.log('退出登录')
}
</script>

<style scoped>
.app-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  height: 60px;
  padding: 0 24px;
  background: #001529;
  color: #fff;
}
.header-right {
  display: flex;
  align-items: center;
  gap: 16px;
}
</style>

6.2 侧边栏 AppSidebar.vue

侧边栏菜单需要和路由保持一致,用 routerroutes 或自己维护菜单配置都可以。

<!-- layouts/components/AppSidebar.vue -->
<template>
  <aside class="app-sidebar">
    <el-menu
      :default-active="activeMenu"
      router
      background-color="#001529"
      text-color="#fff"
    >
      <el-menu-item index="/dashboard">
        <span>仪表盘</span>
      </el-menu-item>
      <el-menu-item index="/user">
        <span>用户管理</span>
      </el-menu-item>
    </el-menu>
  </aside>
</template>

<script setup>
import { computed } from 'vue'
import { useRoute } from 'vue-router'

const route = useRoute()

// 高亮当前路由对应的菜单项
const activeMenu = computed(() => route.path)
</script>

<style scoped>
.app-sidebar {
  width: 200px;
  background: #001529;
}
</style>

要点:

  • router 属性:点击菜单项会直接 router.push(index),无需手动处理
  • default-active 绑定当前路径,实现高亮

6.3 面包屑 Breadcrumb.vue

面包屑需要从当前路由推导出层级,用 route.matched 即可。

<!-- layouts/components/Breadcrumb.vue -->
<template>
  <el-breadcrumb separator="/" class="breadcrumb">
    <el-breadcrumb-item
      v-for="(item, index) in breadcrumbList"
      :key="item.path"
    >
      <!-- 最后一项不跳转 -->
      <router-link v-if="index < breadcrumbList.length - 1" :to="item.path">
        {{ item.meta?.title || item.name || '未命名' }}
      </router-link>
      <span v-else>{{ item.meta?.title || item.name || '未命名' }}</span>
    </el-breadcrumb-item>
  </el-breadcrumb>
</template>

<script setup>
import { computed } from 'vue'
import { useRoute } from 'vue-router'

const route = useRoute()

// 从路由的 matched 自动生成面包屑
const breadcrumbList = computed(() => {
  return route.matched.filter(item => item.meta?.title || item.name)
})
</script>

<style scoped>
.breadcrumb {
  margin-bottom: 16px;
}
</style>

要点:

  • route.matched 是当前路由及其所有父路由的数组,正好对应面包屑层级
  • 最后一项用 <span>,前面的用 <router-link> 方便点击返回

七、常见坑点

坑 1:侧边栏和路由不同步

  • 原因:菜单写死在模板里,路由改了菜单没改
  • 做法:用 router.options.routes 或单独维护菜单配置,和路由保持一致,用 route.path 作为菜单的 index

坑 2:面包屑不显示或显示不对

  • 原因:route.matched 里的路由没有 meta.title
  • 做法:给每个需要出现在面包屑中的路由加上 meta: { title: 'xxx' },根路由如果是 redirect 可以不加或设为 hidden: true

坑 3:刷新后侧边栏高亮错误

  • 原因:default-active 没正确绑定到当前路径
  • 做法:用 computed(() => route.path) 绑定,并且菜单项的 index 和路由的 path 一致

坑 4:布局组件被重复创建

  • 原因:同一个父路由下的子路由切换时,Vue Router 默认会复用父级 Layout
  • 做法:这是正常行为。若需要在切换子路由时强制重挂载 Layout,可以给 router-view:key="route.fullPath",但一般不需要

八、菜单与路由统一:进阶写法

为了不重复维护「路由」和「菜单」,可以统一用路由生成菜单:

// 在 router 里定义好 meta
// 在 AppSidebar 里动态读取
import { useRouter } from 'vue-router'

const router = useRouter()
const menuRoutes = computed(() => {
  const parent = router.options.routes.find(r => r.path === '/')
  return (parent?.children || []).filter(r => !r.meta?.hidden)
})
<el-menu-item
  v-for="item in menuRoutes"
  :key="item.path"
  :index="'/' + item.path"
>
  {{ item.meta?.title }}
</el-menu-item>

这样菜单和路由只维护一份。

九、总结

模块 职责 与路由的关系
Layout 包裹头部、侧边栏、内容区 作为父路由的 component
Header 顶部固定区域 一般与路由无关
Sidebar 菜单导航 使用 routerroute.path 高亮
Breadcrumb 当前路径层级展示 依赖 route.matchedmeta
内容区 子页面内容 <router-view> 渲染

记住三步:

  1. 用嵌套路由,父用 Layout,子用具体页面
  2. 布局拆成 Header、Sidebar、Breadcrumb、router-view 四个区域
  3. 菜单、面包屑都从 routemeta 推导,避免重复配置

如果你希望我把某个小节展开(例如只用原生 div + CSS,或用 Vue 2 + Vue Router 3 版本),可以说一下具体需求,我可以再补一版对应示例。

🔍 本系列专栏导航

一、《路由与布局扫盲篇:Vue Router 实战 | 动态路由、嵌套路由与多级菜单》

二、《路由与布局扫盲篇:登录态与路由守卫 | token 校验、白名单、重定向》

三、《路由与布局扫盲篇:多标签页(Tab)与缓存 | keep-alive、includeexclude、路由 meta》

四、《路由与布局扫盲篇:布局系统 | 头部、侧边栏、内容区、面包屑的拆分与复用》

👉 跟着系列慢慢学,把技术功底扎扎实实地打牢~


学习本就是一场持久战,不需要急着一口吃成胖子。哪怕今天你只记住了一点点,这都是实打实的进步。

后续我还会继续用这种大白话、讲实战方式,带大家扫盲更多前端基础。

关注我,不迷路,咱们把那些曾经模糊的知识点,一个个彻底搞清楚。

如果你觉得这篇内容对你有帮助,不妨点赞+收藏,下次写代码卡壳时,拿出来翻一翻,比搜引擎更靠谱。

我是 Eugene,你的电子学友,我们下一篇干货见~

❌
❌