svg图标原理;面包屑组件代码;tabs代码
1.SVG图标完整流程(极简流程图)
ini
你把 hamburger.svg 放到 src/assets/icons
↓
vite‑plugin‑svg‑icons(启动时扫描文件夹)
↓
读取svg内容 → 包装成 <symbol id="icon‑hamburger">
↓
自动注入隐藏svg到页面DOM(display:none,肉眼看不见)
↓
main.js import 'virtual:svg-icons-register' 【必须!触发插件生效】
↓
你的页面使用组件 <m-svg-icon name="hamburger"/>
↓
组件计算 symbolId → #icon‑hamburger
↓
<use :xlink:href="#icon‑hamburger"> 找到页面里的symbol
↓
渲染图形 → 出现汉堡三条杠 ✅
必记3个坑
- 忘了
import 'virtual:svg-icons-register'→ 全部图标空白 - 修改svg文件,不重启vite → 不更新symbol,图标不变
- svg源码写死 fill="#000" → 组件传color改不了颜色
对应代码位置速查
- vite.config.js →
createSvgIconsPlugin,配置iconDirs图标目录 - main.js →
import 'virtual:svg-icons-register' - src/libs/svg‑icon/index.vue → m‑svg‑icon 组件
- 使用:
<m-svg-icon name="hamburger"/>
回忆之前:
name="hamburger"不要写 icon‑,组件内部自动拼接成#icon‑hamburger
2.面包屑核心来源:$route.matched
$route.matched 是路由匹配数组,保存当前路由每一层级。
举例路由配置
js
const routes = [
{
path: '/',
name: 'home',
meta: { title: '首页' },
children: [
{
path: '/system',
meta: { title: '系统管理' },
children: [
{
path: '/system/user',
meta: { title: '用户管理' }
}
]
}
]
}
]
访问 /system/user,$route.matched 得到数组:
js
[
{ path:'/', meta:{title:'首页'} },
{ path:'/system', meta:{title:'系统管理'} },
{ path:'/system/user', meta:{title:'用户管理'} }
]
面包屑就遍历这个数组,取出每个的 meta.title,拼接:首页 / 系统管理 / 用户管理
Vue3 script‑setup 简易伪代码
vue
<template>
<div class="breadcrumb">
<span v-for="item in breadList" :key="item.path">
{{ item.meta.title }}
<span v-if="!isLast(item)"> / </span>
</span>
</div>
</template>
<script setup>
import { useRoute } from 'vue-router'
const route = useRoute()
// 直接拿到层级数组
const breadList = route.matched
</script>
关键点:
- 面包屑不会生成不在路由层级里的页面 ,完全靠路由配置的
meta.title - 最后一项(当前页面)一般不给跳转链接,前面层级加
to跳转 - 若依就是基于这套逻辑封装的
el‑breadcrumb
三者再快速复盘(面试脑图)
- 汉堡按钮:控制侧边栏展开/收起(按钮)
- 侧边导航栏:全部功能入口,跳转任意页面,读取路由全部菜单
- 面包屑 :
route.matched,只看当前这条路径的层级,回退上层 - Tabs标签页:保存已经打开过的页面记录,可以来回切换、关闭
容易混淆点
- 侧边导航:全部菜单(完整树)
- 面包屑:当前路径的一条链(只一条)
面试简答:面包屑怎么实现? 答:通过 vue‑router 的
route.matched获取路由匹配记录,遍历数组取出路由元信息meta.title渲染,除最后一项外绑定路由跳转,实现路径导航。
3.Tabs标签页(若依顶部可关闭页签)
效果:【首页】【用户管理】【角色管理】 ×
核心:自己维护一个数组,不是路由自带的 。
route.matched给面包屑用;Tabs是手动存你访问过的页面。
核心逻辑
- 访问路由的时候,把页面信息push进数组(不要重复添加)
- 循环数组渲染标签
- 点击标签:跳转到对应路由
- 点关闭×:把这条从数组删掉
- 不能关闭首页
伪代码 Vue3 setup
vue
<template>
<div class="tabs">
<span
v-for="tab in tabList"
:key="tab.path"
@click="$router.push(tab.path)"
>
{{ tab.title }}
<!-- 首页不显示关闭按钮 -->
<span v-if="tab.path !== '/' " @click="closeTab(tab)">×</span>
</span>
</div>
</template>
<script setup>
import { ref, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
const route = useRoute()
const router = useRouter()
// 存已经打开的页签,这个就是tabs数据源
const tabList = ref([
{ path: '/', title: '首页' } // 默认首页一定存在
])
// 监听路由变化,每次跳转路由触发
watch(
() => route.path,
() => {
// 判断这个页签是否已经存在,不存在才push
const has = tabList.value.some(item => item.path === route.path)
if (!has) {
tabList.value.push({
path: route.path,
title: route.meta.title
})
}
},
{ immediate: true }
)
// 关闭标签
const closeTab = (tab) => {
// 删除当前tab
tabList.value = tabList.value.filter(item => item.path !== tab.path)
}
</script>
三个组件数据来源对比(重点,面试常考)
| 组件 | 数据来自哪里 |
|---|---|
| 侧边菜单导航 | 完整路由树 routes,过滤掉不需要显示的路由 |
| 面包屑 | route.matched,当前路由向上的整条链路 |
| Tabs标签页 | 自己维护数组,监听路由跳转手动收集访问记录 |
关键点:
- 面包屑的数据路由自带;
- Tabs路由没有给你存,需要自己写状态保存访问记录 ,刷新页面会全部消失(若依用
sessionStorage持久化,刷新还在)
完整后台布局三件套总结
-
汉堡hamburger按钮 点击切换侧边栏展开/收起状态(存pinia)
-
侧边导航菜单 遍历完整路由配置树,渲染全部菜单,点击跳转页面
-
面包屑 breadcrumb
route.matched,拿到当前路径链条,渲染路径导航 -
Tabs多标签页 watch监听路由变化,手动维护数组,存放访问过的页面,支持切换关闭
面试简短口述版本
后台管理系统常见三个导航组件: 1.侧边菜单:遍历全部路由树生成,作为系统功能入口; 2.面包屑:使用
route.matched获取当前路由匹配层级,取出meta标题渲染路径,实现上层跳转; 3.Tabs标签页:自己维护数组,监听路由变化收集访问过的页面,支持切换关闭,一般存sessionStorage做刷新持久化。