动态路由+能支持iframe嵌入
动态路由+能支持iframe嵌入
“动态路由 + iframe 内嵌”的工作机制与关键点,帮你快速看清楚从配置→路由注入→渲染的全流程。
总体流程
-
静态和动态菜单源合并后统一转换为可注入的路由对象并注册到路由树。
-
若路由项的
meta.type
指定为支持内嵌的类型(本项目现有:iframe
<打开新标签页> 与embedding_iframe
<页面内嵌>),会被转换为一个内部访问路径/i/${name}
,并将原始外链地址存入meta.url
。 -
页面渲染时,真实的页面不是常规
view
组件,而是通过iframeView
组件读取meta.url
,以<iframe>
内嵌方式展示。
关键文件与职责
-
路由注入与转换:
src/router/index.js
- 合并菜单源:
- 静态:
userRoutes
(来自src/config/route.js
) - 动态:
MENU
(来自tool.data.get('MENU')
)
- 静态:
- 鉴权过滤:
treeFilter(userRoutes, func)
会按meta.role
做权限控制。 - 转换核心:
filterAsyncRouter(routerMap)
- 识别外链型菜单并改造为内嵌路由:
1
2
3
4
5//处理外部链接特殊路由
if (item.meta.type == 'iframe' || item.meta.type == 'embedding_iframe') {
item.meta.url = item.path;
item.path = `/i/${item.name}`;
}- 原
path
(外链 URL)被保存到meta.url
- 新
path
统一变成/i/${name}
- 原
- 统一生成标准路由对象并按
layout
父路由动态注册:1
2
3
4
5
6
7
8var route = {
path: item.path,
name: item.name,
meta: item.meta,
redirect: item.redirect,
children: item.children ? filterAsyncRouter(item.children) : null,
component: loadComponent(item.component),
};
- 识别外链型菜单并改造为内嵌路由:
- 首次进入时的动态注册控制:
isGetRouter
保证只注入一次;注入完成后才加 404。 - 注意:如果存在“进入前打开新标签”的逻辑,通常写在
beforeEach
中根据meta.type==='iframe'
执行window.open(...)
。若你要所有 iframe 内嵌显示,应避免在守卫里拦截打开新页,仅依赖iframeView
渲染。
- 合并菜单源:
-
菜单与链接规范化:
src/layout/index.vue
- 菜单二次处理
filterUrl
:确保左侧菜单点击时,外链类项的path
也会落在/i/${name}
,与路由规则一致。1
2
3if (item.meta.type == 'iframe' || item.meta.type == 'embedding_iframe') {
item.path = `/i/${item.name}`;
}
- 菜单二次处理
-
iframe 渲染容器:
src/layout/components/iframeView.vue
- 只在当前路由
meta.type
为外链内嵌类型时显示 iframe 容器:1
2
3<div v-show="$route.meta.type=='iframe' || $route.meta.type=='embedding_iframe'" class="iframe-pages">
<iframe v-for="item in iframeList" :key="item.meta.url" v-show="$route.meta.url==item.meta.url" :src="item.meta.url" frameborder='0'></iframe>
</div> - 进入外链路由时,会将当前路由压入
iframeList
,从而支持多标签/刷新等行为:1
2
3
4
5
6
7
8
9
10
11if(route.meta.type == 'iframe' || route.meta.type == 'embedding_iframe'){
if(this.ismobile || !this.layoutTags){
this.$store.commit("setIframeList", route)
}else{
this.$store.commit("pushIframeList", route)
}
}else{
if(this.ismobile || !this.layoutTags){
this.$store.commit("clearIframeList")
}
} - 对应的持久化列表在
src/store/modules/iframe.js
,包含setIframeList/pushIframeList/refreshIframe/clearIframeList
等。
- 只在当前路由
路由配置怎么写(例)
在 src/config/route.js
静态菜单里,外链菜单这样写:
1 | { |
-
name
:必须唯一,用来生成内部访问路径/i/compassindustry
-
path
:外部真实 URL -
meta.type
:使用iframe
或embedding_iframe
任一都可按当前代码支持内嵌 -
渲染流程:点击菜单 → 实际跳转到
/i/compassindustry
→iframeView
根据meta.url
内嵌加载外链
常见问题与排查
-
“No match found for location with path ‘/i/xxx’”:通常是该项路由尚未被注入。确保:
- 该菜单项包含在
userRoutes
或后端返回的MENU
中; - 名称
name
与菜单里点击的一致(由name
决定/i/${name}
); - 首次加载时不要过早跳转到尚未注册的
/i/...
,或保证动态注入在导航前完成。
- 该菜单项包含在
-
点击外链被新开标签页:检查路由守卫里是否有针对
meta.type === 'iframe'
的window.open
拦截。若你希望“全部内嵌”,请移除或改为仅对特定类型生效。 -
需要新支持的类型:在
filterAsyncRouter
与iframeView.vue
的判断里加上你的meta.type
即可。