214 lines
5.7 KiB
Vue
214 lines
5.7 KiB
Vue
<template>
|
||
<div id="main-app">
|
||
<header class="app-header">
|
||
<div class="logo" @click="$router.push('/')">
|
||
<h1>MicroApp 主应用</h1>
|
||
</div>
|
||
<nav class="nav-links">
|
||
<router-link to="/home">首页</router-link>
|
||
<router-link to="/child-app">Vue2 子应用</router-link>
|
||
<router-link to="/vue3-app">Vue3 子应用</router-link>
|
||
<router-link to="/mock-app">💥主vs子</router-link>
|
||
<router-link to="/mock-app-2">🔵子vs子</router-link>
|
||
</nav>
|
||
</header>
|
||
<main class="app-main">
|
||
<router-view />
|
||
</main>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
// 根组件 — 提供全局布局(头部导航 + 内容区)
|
||
</script>
|
||
|
||
<style>
|
||
/* ============================================
|
||
全局样式重置(主应用)
|
||
注意:以下使用了 h2 / button / p / table / code
|
||
等通用选择器。在微前端场景中,这些选择器会:
|
||
1. 影响主应用自身的元素 ✅ (预期行为)
|
||
2. 泄漏到 iframe: false 的子应用中 ⚠️ (样式冲突)
|
||
============================================ */
|
||
* {
|
||
margin: 0;
|
||
padding: 0;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
html, body {
|
||
height: 100%;
|
||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
|
||
'Helvetica Neue', Arial, 'Microsoft YaHei', sans-serif;
|
||
color: #333;
|
||
background: #f5f6f7;
|
||
}
|
||
|
||
#app {
|
||
height: 100%;
|
||
}
|
||
|
||
/* ---------- 主应用内容区样式(限定在主应用自身页面内) ---------- */
|
||
/*
|
||
问题:子应用在 iframe: false 模式下,DOM 直接嵌入主文档,
|
||
挂在 <router-view> → <ChildApp.vue> → <micro-app> 下面,
|
||
而 <router-view> 又在 .app-main 内部。
|
||
所以 .app-main h2 这种「后代选择器」会穿透到子应用的 h2。
|
||
|
||
之前(会泄漏): h2 { ... } ← 没有作用域,100% 泄漏
|
||
中间(仍泄漏): .app-main h2 { ... } ← 子应用也在 .app-main 里!
|
||
现在(已修复): .app-main > :not(.child-app-wrapper) h2 { ... }
|
||
↑ 用子选择器 > 只匹配 .app-main 的直接子元素,
|
||
再用 :not(.child-app-wrapper) 排除子应用容器,
|
||
彻底阻断样式进入 micro-app 内部
|
||
|
||
DOM 层级示意:
|
||
.app-main
|
||
├── div.home-page ← :not(.child-app-wrapper) → ✅ 样式生效
|
||
└── div.child-app-wrapper ← 被 :not() 排除 → ❌ 样式不生效
|
||
└── <micro-app>
|
||
└── 子应用 h2/button/p... ← 安全,不受主应用样式影响
|
||
*/
|
||
|
||
/* 冲突点 ①:h2 — 主应用想要紫色渐变标题 */
|
||
.app-main > :not(.child-app-wrapper) h2 {
|
||
font-family: 'Microsoft YaHei', sans-serif !important;
|
||
font-size: 20px !important;
|
||
color: #667eea !important;
|
||
border-bottom: 2px solid #667eea !important;
|
||
padding-bottom: 8px !important;
|
||
margin: 12px 0 !important;
|
||
text-transform: none !important;
|
||
border-left: none !important;
|
||
}
|
||
|
||
|
||
/* 冲突点 ②:button — 主应用想要紫色直角按钮 */
|
||
.app-main > :not(.child-app-wrapper) button {
|
||
background: #667eea !important;
|
||
color: #fff !important;
|
||
border: none !important;
|
||
border-radius: 4px !important;
|
||
padding: 6px 16px !important;
|
||
font-size: 13px !important;
|
||
font-weight: 500 !important;
|
||
cursor: pointer !important;
|
||
letter-spacing: 0 !important;
|
||
}
|
||
.app-main > :not(.child-app-wrapper) button:hover {
|
||
background: #5a6fd6 !important;
|
||
transform: none !important;
|
||
}
|
||
|
||
/* 冲突点 ③:p 段落 — 主应用想要紧凑排版 */
|
||
.app-main > :not(.child-app-wrapper) p {
|
||
font-size: 14px !important;
|
||
line-height: 1.6 !important;
|
||
color: #444 !important;
|
||
margin-bottom: 8px !important;
|
||
}
|
||
|
||
/* 冲突点 ④:table — 主应用想要紫色无边框表格 */
|
||
.app-main > :not(.child-app-wrapper) table {
|
||
border-collapse: collapse !important;
|
||
width: 100% !important;
|
||
border: none !important;
|
||
}
|
||
.app-main > :not(.child-app-wrapper) th {
|
||
background: #667eea !important;
|
||
color: white !important;
|
||
padding: 8px !important;
|
||
text-align: left !important;
|
||
font-size: 13px !important;
|
||
}
|
||
.app-main > :not(.child-app-wrapper) td {
|
||
padding: 6px 8px !important;
|
||
border-bottom: 1px solid #e0e0e0 !important;
|
||
font-size: 13px !important;
|
||
}
|
||
|
||
/* 冲突点 ⑤:code 标签 — 主应用想要紫色代码块 */
|
||
.app-main > :not(.child-app-wrapper) code {
|
||
background: #e8e0f0 !important;
|
||
color: #5e35b1 !important;
|
||
border: 1px solid #b39ddb !important;
|
||
padding: 2px 6px !important;
|
||
border-radius: 3px !important;
|
||
font-size: 13px !important;
|
||
}
|
||
|
||
/* 主应用布局 */
|
||
.main-style-source {
|
||
display: inline-block;
|
||
padding: 2px 10px;
|
||
border-radius: 4px;
|
||
font-size: 11px;
|
||
font-weight: bold;
|
||
margin-left: 8px;
|
||
}
|
||
.label-main {
|
||
background: #667eea;
|
||
color: white;
|
||
}
|
||
|
||
#main-app {
|
||
display: flex;
|
||
flex-direction: column;
|
||
height: 100%;
|
||
}
|
||
|
||
/* 头部导航 */
|
||
.app-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
height: 56px;
|
||
padding: 0 24px;
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
color: #fff;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.logo {
|
||
cursor: pointer;
|
||
}
|
||
|
||
.logo h1 {
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
letter-spacing: 1px;
|
||
}
|
||
|
||
.nav-links {
|
||
display: flex;
|
||
gap: 8px;
|
||
}
|
||
|
||
.nav-links a {
|
||
color: rgba(255, 255, 255, 0.85);
|
||
text-decoration: none;
|
||
padding: 6px 16px;
|
||
border-radius: 6px;
|
||
font-size: 14px;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.nav-links a:hover {
|
||
color: #fff;
|
||
background: rgba(255, 255, 255, 0.15);
|
||
}
|
||
|
||
.nav-links a.router-link-active {
|
||
color: #fff;
|
||
background: rgba(255, 255, 255, 0.2);
|
||
font-weight: 500;
|
||
}
|
||
|
||
/* 主内容区 */
|
||
.app-main {
|
||
flex: 1;
|
||
overflow: auto;
|
||
}
|
||
</style>
|