在微前端(如 qiankun、wujie 等)架构中,主应用(基座)和子应用(微应用)通常是独立开发、独立部署的。由于两个项目在开发时互不知晓对方的全局 CSS,当子应用被动态挂载到主应用中时,**高度塌陷、双重滚动条、页面被撑破**是最常遇到的三大布局灾难。本文以 Flex 布局 + 绝对定位为例,总结一套通用的高度控制方案。
一、 常见问题及原因分析
- 高度塌陷 (Height Collapse)
现象:子应用加载后,高度为 0 或者只显示了部分内容,下方的页面留白大片,或者子应用内容无法撑开。
原因:主应用的挂载容器没有设定明确的高度,或者没有正确处理 Flex 布局。子应用内部的根元素(如 `#root`, `#app`)默认高度由内容撑开,如果内部使用了绝对定位或 100% 高度,而此时父容器高度未定,就会塌陷。
- 双重滚动条 (Double Scrollbars)
现象:浏览器右侧出现一条最外层的滚动条,而子应用内部(比如某个列表区域)也出现了一条滚动条。两个滚动条互相干扰。
原因:主应用和子应用都设置了 `height: 100vh` 或 `overflow: auto`。子应用内容过长时,撑开了自身容器,进而撑开了主应用的容器,导致浏览器级别和子应用内部同时出现滚动条。
- 页面撑破 (Page Overflow)
现象:子应用内容很长时,整个页面被无限拉长,主应用固定在顶部的 Header(导航栏)被滚走,或者主应用底部被顶出屏幕外。
原因:主应用的子应用容器没有限制最大高度,也没有设置 `overflow: hidden`,内容溢出导致整个 document 流被撑大。
二、 最佳实践:主应用侧 CSS 布局方案
为了避免上述问题,主应用应该采取**"画地为牢"**的策略,给子应用划定一个严格的高度和宽度范围,让子应用在这个盒子里自己折腾。
假设主应用的 DOM 结构如下:
```html
<div class="main-app">
<header class="header">顶部导航栏(固定高度)</header>
<!-- 子应用挂载容器 -->
<div id="subapp-container" class="subapp-container"></div>
</div>
```
核心 CSS 规范:
- 主应用最外层容器 (`.main-app`)
```css
.main-app {
display: flex;
flex-direction: column; /* 垂直排列 */
min-height: 100vh; /* 保证至少占满一屏 */
}
```
- 子应用挂载容器 (`.subapp-container`)
```css
.subapp-container {
flex: 1; /* 占据 Header 剩下的所有空间 */
min-height: 0; /* 【极其关键】:防止被内容撑大,允许内部滚动 */
position: relative; /* 为内部子应用的绝对定位提供参考 */
/* 如果希望子应用内滚,可以在这里加上 overflow: hidden; 彻底锁死 */
}
```
- 微前端框架的包装容器 (`.subapp-container > div`)
*(注:这里假设微前端框架会在容器内生成一层 wrapper,如 qiankun 的 `_qiankun_microapp_wrapper...`)*
```css
.subapp-container > div {
position: absolute; /* 绝对定位 */
inset: 0; /* top/right/bottom/left 全为 0 */
/* 作用:强制让包装容器贴合父容器,无论子应用内部多高,绝不撑破主应用 */
}
```
三、 子应用侧需要配合的 CSS
主应用已经给定了明确的高度和绝对定位,子应用在开发时也需要遵循一定的规范,才能真正做到**内部滚动**而不是**撑破页面**。
假设子应用的 DOM 结构:
```html
<div id="root">
<div class="app-layout">
<div class="content">...</div>
</div>
</div>
```
推荐的子应用 CSS:
```css
/* 1. 根节点必须撑满主应用给予的 wrapper 容器 */
#root {
height: 100%;
width: 100%;
}
/* 2. 子应用内部如果希望右侧独立滚动,只需在特定的内容区域设置 */
.app-layout {
display: flex;
flex-direction: column;
height: 100%;
}
.content {
flex: 1;
overflow-y: auto; /* 【关键】:只让这块区域出现滚动条 */
min-height: 0;
}
```
四、 核心知识点提炼 (Cheatsheet)
- **`min-height: 100vh` 不等于 `height: 100vh`**:
* `min-height: 100vh` 允许页面被撑高,可能引发外层滚动条。
* `height: 100vh` 严格锁定屏幕高度,超出即溢出。
- **Flex 布局下的 `min-height: 0` 至关重要**:
* 在 Flex 容器中,子元素默认 `min-height: auto`(会被内容撑大)。只有将其设为 `0`,子元素才会在超出时触发 `overflow: auto` 而不是撑大父级。
- **绝对定位 + `inset: 0` 是最佳隔离手段**:
* 对微前端挂载的 DOM 节点使用绝对定位(配合父级 `relative`),可以无视子应用内部复杂的层级和高度,强行将其限制在主应用划定的边界内。
- **先画边界,再谈滚动**:
* 永远记住:主应用负责提供"画框"(固定高度、绝对定位),子应用负责在"画框内作画"(内部设置 `height: 100%` 和 `overflow-y: auto`)。