整体渲染思路:
TabMenu创建 → 解析路由配置 → 生成tabPanes数据
TabPanes接收数据 → 遍历创建el-tab-pane
用户点击标签 → activeName改变
Vue发现activeName匹配 → 调用对应的componentName函数
loadView执行 → require()加载组件文件
组件加载完成 → 渲染到页面
keep-alive缓存 → 切换时保持状态
// TabMenu.vue
<template>
<div>
<TabPanes :key="$route.path" :tabPanes.sync="tabPanes" />
</div>
</template>
<script>
import TabPanes from "@/components/TabPane/tab-pane";
function loadView(view) {
return resolve => require([`@/views/${view}`], resolve);
}
export default {
components: {
TabPanes
},
data() {
return {
tabPanes: null
};
},
created() {
this.init();
},
methods: {
init() {
const tabs = this.$route.meta.tabs;
this.tabPanes = tabs.map(item => {
return {
title: item.name,
name: item.code,
componentName: loadView([item.component])
};
});
}
},
watch: {
"$route.path": function(newVal, oldVal) {
this.init();
}
}
};
</script>
// tab-pane.vue
<template>
<el-tabs
v-model="activeName"
:type="tabType"
@tab-click="tabClick"
class="tab-panes"
>
<template>
<el-tab-pane
v-for="(tab, index) in tabPanes"
:key="tab.title+index"
:label="tab.title"
:lazy="true"
:name="tab.name"
>
<keep-alive>
<component
class="content"
v-if="activeName === tab.name"
:is="tab.componentName"
/>
</keep-alive>
</el-tab-pane>
</template>
</el-tabs>
</template>
<script>
//这是tabPane要显示的组件
export default {
name: "TabPanes",
props: {
tabPanes: {
type: Array,
default: () => []
},
tabType: {
type: String,
default: ""
}
},
data() {
return {
activeName: null
};
},
created() {
this.setActiveTab();
},
mounted(){
this.setActiveTab();
},
watch: {
activeName: {
handler(val) {
if (val) {
this.$router.replace({
query: { tab: val },
params: { ...this.$route.params }
});
}
},
},
"$route.query.tab"(val) {
this.activeName = val || '';
},
tabPanes: {
handler(val) {
if (val.length > 0 && !this.$route.query.tab) {
this.activeName = val[0].name;
}
}
},
},
methods: {
setActiveTab() {
const tabFromQuery = this.$route.query.tab;
const isValidTab = tabFromQuery && this.tabPanes.some(tab => tab.name === tabFromQuery);
this.activeName = isValidTab ? tabFromQuery : (this.tabPanes[0] && this.tabPanes[0].name || '');
},
tabClick(tab) {
this.activeName = tab.name;
},
}
};
</script>
<style lang="scss" scoped>
.el-tabs {
position: absolute;
inset: 0;
overflow: overlay;
& ::v-deep .el-tabs__header {
padding: 0;
position: relative;
margin: 0 0 0 10px;
border-top: 1px solid #bfcbd9;
}
& ::v-deep .el-tabs__nav-wrap {
background-color: #fff !important;
padding-left: 10px;
}
&::-webkit-scrollbar {
width: 10px;
background: transparent;
}
&::-webkit-scrollbar-thumb {
border-radius: 9px;
background-color: #c0c0c0;
}
}
</style>
TabMenu.vue 是在哪里被调用的?
结论:TabMenu.vue 不是在前端代码中直接调用的,而是通过后端返回的动态路由配置来加载的!
整个过程是:
1.用户访问某个URL(比如 /xxx )
2.权限系统检查用户是否有该页面权限
3.后端返回菜单配置,其中包含:
{
"path": "/xxx",
"component": "component/TabMenu",
"meta": {
"tabs": [
{"code": "xxx", "name": "tab页面名称", "component": "xx/xx/xx"}
]
}
}
4.前端动态加载 component/TabMenu.vue 组件
5.TabMenu执行,解析 meta.tabs 配置,渲染子页面
这就是为什么你找不到直接调用的原因!
TabMenu是动态路由系统的一部分,由后端菜单配置驱动,而不是在前端代码中硬编码引用的。
这种设计的优势:
权限灵活: 可以动态控制谁可以访问哪些页面
菜单可配置: 管理员可以配置页面结构,无需修改前端代码
**组件按需加载:**只在需要时才加载对应的Vue组件