样式冲突问题模板
This commit is contained in:
94
src/App.vue
94
src/App.vue
@@ -8,6 +8,8 @@
|
||||
<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">
|
||||
@@ -21,7 +23,13 @@
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* 全局样式重置 */
|
||||
/* ============================================
|
||||
全局样式重置(主应用)
|
||||
注意:以下使用了 h2 / button / p / table / code
|
||||
等通用选择器。在微前端场景中,这些选择器会:
|
||||
1. 影响主应用自身的元素 ✅ (预期行为)
|
||||
2. 泄漏到 iframe: false 的子应用中 ⚠️ (样式冲突)
|
||||
============================================ */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
@@ -40,6 +48,90 @@ html, body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* ---------- 冲突演示:主应用全局样式 ---------- */
|
||||
/* 这些通用选择器如果被子应用的同名选择器覆盖,就会发生冲突 */
|
||||
|
||||
/* 冲突点 ①:h2 — 主应用想要紫色渐变标题 */
|
||||
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 — 主应用想要紫色直角按钮 */
|
||||
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;
|
||||
}
|
||||
button:hover {
|
||||
background: #5a6fd6 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
|
||||
/* 冲突点 ③:p 段落 — 主应用想要紧凑排版 */
|
||||
p {
|
||||
font-size: 14px !important;
|
||||
line-height: 1.6 !important;
|
||||
color: #444 !important;
|
||||
margin-bottom: 8px !important;
|
||||
}
|
||||
|
||||
/* 冲突点 ④:table — 主应用想要紫色无边框表格 */
|
||||
table {
|
||||
border-collapse: collapse !important;
|
||||
width: 100% !important;
|
||||
border: none !important;
|
||||
}
|
||||
th {
|
||||
background: #667eea !important;
|
||||
color: white !important;
|
||||
padding: 8px !important;
|
||||
text-align: left !important;
|
||||
font-size: 13px !important;
|
||||
}
|
||||
td {
|
||||
padding: 6px 8px !important;
|
||||
border-bottom: 1px solid #e0e0e0 !important;
|
||||
font-size: 13px !important;
|
||||
}
|
||||
|
||||
/* 冲突点 ⑤:code 标签 — 主应用想要紫色代码块 */
|
||||
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;
|
||||
|
||||
@@ -32,11 +32,10 @@ export interface SubAppConfig {
|
||||
export const subApps: SubAppConfig[] = [
|
||||
{
|
||||
name: 'vue2-app',
|
||||
// TODO: 替换为你的 Vue 2 子应用实际地址
|
||||
url: 'http://localhost:5173/',
|
||||
baseroute: '/child-app',
|
||||
// Vite 子应用必须开启 iframe 模式
|
||||
// (with 沙箱的 new Function() 不支持 ES Module 的 import/export 语法)
|
||||
// Vite 子应用必须开启 iframe 沙箱
|
||||
// my-vue2-app 虽然是 Vue 2,但用 Vite 构建,输出 ES Module
|
||||
iframe: true,
|
||||
keepAlive: true,
|
||||
routerMode: 'native'
|
||||
@@ -49,6 +48,35 @@ export const subApps: SubAppConfig[] = [
|
||||
iframe: true,
|
||||
keepAlive: true,
|
||||
routerMode: 'native'
|
||||
},
|
||||
{
|
||||
name: 'mock-app',
|
||||
url: 'http://localhost:5174/',
|
||||
baseroute: '/mock-app',
|
||||
// ============================================
|
||||
// ⚠️ 样式冲突演示专用:
|
||||
// iframe: false → 子应用 DOM 直接嵌入主文档
|
||||
// disableScopecss: true → 关闭样式隔离,双方样式互相泄漏
|
||||
// 这是一个纯 HTML 页面(无 JS 框架),不需要 ES Module 支持
|
||||
// ============================================
|
||||
iframe: false,
|
||||
disableScopecss: true,
|
||||
keepAlive: true,
|
||||
routerMode: 'native'
|
||||
},
|
||||
{
|
||||
name: 'mock-app-2',
|
||||
url: 'http://localhost:5175/',
|
||||
baseroute: '/mock-app-2',
|
||||
// ============================================
|
||||
// ⚠️ 子应用间样式冲突演示:
|
||||
// 与 mock-app 相同:iframe: false, disableScopecss: true
|
||||
// 用于验证:前一个子应用的样式是否会影响后一个子应用
|
||||
// ============================================
|
||||
iframe: false,
|
||||
disableScopecss: true,
|
||||
keepAlive: true,
|
||||
routerMode: 'native'
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@@ -27,6 +27,18 @@ const routes = [
|
||||
path: '/vue3-app/:page*',
|
||||
name: 'vue3App',
|
||||
component: () => import('@/views/ChildApp.vue')
|
||||
},
|
||||
{
|
||||
// Mock 子应用路由 — 样式冲突演示
|
||||
path: '/mock-app/:page*',
|
||||
name: 'mockApp',
|
||||
component: () => import('@/views/ChildApp.vue')
|
||||
},
|
||||
{
|
||||
// Mock 子应用 2 路由 — 子应用间样式冲突演示
|
||||
path: '/mock-app-2/:page*',
|
||||
name: 'mockApp2',
|
||||
component: () => import('@/views/ChildApp.vue')
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@@ -7,6 +7,49 @@
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- ============================================
|
||||
样式冲突演示说明
|
||||
============================================ -->
|
||||
<div class="conflict-demo-notice">
|
||||
<div class="notice-header">
|
||||
⚠️ 样式冲突演示区
|
||||
</div>
|
||||
<div class="notice-body">
|
||||
<p><strong>演示目的:</strong>展示微前端中<span class="highlight-bad">样式隔离关闭</span>时的 CSS 冲突现象。</p>
|
||||
<div class="demo-setup">
|
||||
<div class="demo-card isolated">
|
||||
<div class="demo-title">🛡️ Vue3 子应用(正常)</div>
|
||||
<div class="demo-config">
|
||||
<code>iframe: true</code>
|
||||
<code>disableScopecss: false</code>
|
||||
</div>
|
||||
<p class="demo-desc">iframe 提供浏览器原生隔离,样式完全隔离</p>
|
||||
<router-link to="/vue3-app" class="demo-link">查看正常效果 →</router-link>
|
||||
</div>
|
||||
<div class="demo-card conflict">
|
||||
<div class="demo-title">💥 Vue2 子应用(冲突)</div>
|
||||
<div class="demo-config">
|
||||
<code>iframe: false</code>
|
||||
<code>disableScopecss: true</code>
|
||||
</div>
|
||||
<p class="demo-desc">DOM 嵌入主文档 + 样式隔离关闭 → 样式互相泄漏</p>
|
||||
<router-link to="/child-app" class="demo-link">查看冲突效果 →</router-link>
|
||||
</div>
|
||||
</div>
|
||||
<div class="conflict-elements">
|
||||
<span class="label">冲突元素:</span>
|
||||
<code>h2</code> <code>button</code> <code>p</code> <code>table</code> <code>code</code>
|
||||
</div>
|
||||
<p class="notice-hint">
|
||||
👆 先点击右边的「查看冲突效果」,进入子应用页面后,观察 h2/button/table 等元素的样式变化。
|
||||
你会看到主应用的「紫色系」样式和子应用的「橙色系」样式<span class="highlight-bad">同时作用</span>在同一元素上。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ============================================
|
||||
已接入的子应用列表
|
||||
============================================ -->
|
||||
<div class="sub-app-list">
|
||||
<h3>已接入的子应用</h3>
|
||||
<div class="cards">
|
||||
@@ -164,4 +207,160 @@ import { subApps } from '@/config/subApps'
|
||||
border-radius: 3px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
样式冲突演示区块样式
|
||||
============================================ */
|
||||
.conflict-demo-notice {
|
||||
margin-top: 16px;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
border: 2px solid #ff9800;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.notice-header {
|
||||
background: linear-gradient(135deg, #ff9800, #f57c00);
|
||||
color: #fff;
|
||||
padding: 12px 20px;
|
||||
font-weight: 700;
|
||||
font-size: 15px;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.notice-body {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.notice-body p {
|
||||
font-size: 14px;
|
||||
color: #555;
|
||||
line-height: 1.8;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.highlight-bad {
|
||||
background: #fff3e0;
|
||||
color: #e65100;
|
||||
padding: 2px 8px;
|
||||
border-radius: 3px;
|
||||
font-weight: 600;
|
||||
border: 1px solid #ffcc80;
|
||||
}
|
||||
|
||||
/* 演示卡片容器 */
|
||||
.demo-setup {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
margin: 16px 0;
|
||||
}
|
||||
|
||||
.demo-card {
|
||||
flex: 1;
|
||||
padding: 16px;
|
||||
border-radius: 10px;
|
||||
border: 2px solid transparent;
|
||||
}
|
||||
|
||||
.demo-card.isolated {
|
||||
background: #f0faf3;
|
||||
border-color: #42b883;
|
||||
}
|
||||
|
||||
.demo-card.conflict {
|
||||
background: #fff3e0;
|
||||
border-color: #ff6b35;
|
||||
}
|
||||
|
||||
.demo-title {
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 8px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.demo-config {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.demo-config code {
|
||||
background: #333;
|
||||
color: #4ec9b0;
|
||||
padding: 2px 8px;
|
||||
border-radius: 3px;
|
||||
font-size: 12px;
|
||||
font-family: 'Consolas', 'Courier New', monospace;
|
||||
}
|
||||
|
||||
.demo-desc {
|
||||
font-size: 13px;
|
||||
color: #777;
|
||||
margin-bottom: 10px !important;
|
||||
line-height: 1.6 !important;
|
||||
}
|
||||
|
||||
.demo-link {
|
||||
display: inline-block;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
padding: 6px 16px;
|
||||
border-radius: 6px;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.demo-card.isolated .demo-link {
|
||||
background: #42b883;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.demo-card.conflict .demo-link {
|
||||
background: #ff6b35;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.demo-link:hover {
|
||||
opacity: 0.85;
|
||||
transform: translateX(2px);
|
||||
}
|
||||
|
||||
/* 冲突元素标签 */
|
||||
.conflict-elements {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
flex-wrap: wrap;
|
||||
margin: 12px 0;
|
||||
padding: 10px 14px;
|
||||
background: #fff8e1;
|
||||
border-radius: 8px;
|
||||
border-left: 4px solid #ff9800;
|
||||
}
|
||||
|
||||
.conflict-elements .label {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #e65100;
|
||||
}
|
||||
|
||||
.conflict-elements code {
|
||||
background: #333;
|
||||
color: #ffab40;
|
||||
padding: 2px 8px;
|
||||
border-radius: 3px;
|
||||
font-size: 12px;
|
||||
font-family: 'Consolas', 'Courier New', monospace;
|
||||
}
|
||||
|
||||
.notice-hint {
|
||||
background: #fff3e0 !important;
|
||||
padding: 12px 16px !important;
|
||||
border-radius: 8px !important;
|
||||
font-size: 14px !important;
|
||||
color: #e65100 !important;
|
||||
line-height: 1.8 !important;
|
||||
border: 1px dashed #ffcc80;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user