Files
microapp-vue3-interview/docs/interview-question-map-layout.md

243 lines
6.6 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 前端面试题全屏三区布局header + 地图 + 操作栏)且无滚动条
## 题目描述
你需要实现一个**微前端子应用**的地图页面,布局要求如下:
```
┌────────────────────────────────┐
│ sub-app-header │ ← 高 48px始终可见
│ 🟢 Vue3 子应用 [首页][关于][地图] │
├────────────────────────────────┤
│ │
│ │
│ 🗺️ 高德地图 │ ← 占满剩余空间
│ │
│ │
├────────────────────────────────┤
│ 🗺️ 高德地图 [📍回到默认] [🎯我的位置] │ ← 高 52px始终可见
└────────────────────────────────┘
```
**核心要求:**
1. 页面**不能出现滚动条**,三个区域必须同时完整显示在视口内
2. 地图区域需正确渲染(高德地图 JSAPI 要求容器有明确的像素尺寸)
3. 该应用既要能在**微前端环境**中嵌入运行,也要能**独立运行**
---
## 初始代码(有问题)
```vue
<!-- App.vue -->
<template>
<div id="microapp-vue3-container">
<header class="sub-app-header">
<h1>🟢 Vue3 子应用</h1>
<nav>
<router-link to="/">首页</router-link>
<router-link to="/about">关于</router-link>
<router-link to="/map">地图</router-link>
</nav>
</header>
<main class="sub-app-main">
<router-view />
</main>
</div>
</template>
<style>
body {
margin: 0;
}
#microapp-vue3-container {
display: flex;
flex-direction: column;
height: 100vh;
overflow: hidden;
background: #fafafa;
}
.sub-app-header {
height: 48px;
flex-shrink: 0;
/* ...其他样式省略 */
}
.sub-app-main {
flex: 1;
display: flex;
flex-direction: column;
min-height: 0;
overflow-y: auto;
}
</style>
```
```vue
<!-- MapView.vue 地图页面 -->
<template>
<div class="map-page">
<div v-if="loading" class="map-status"> 地图加载中...</div>
<div v-else-if="error" class="map-status map-error"> {{ error }}</div>
<div ref="containerRef" class="map-container" />
<div class="map-controls">
<span>🗺 高德地图</span>
<div>
<button @click="resetView">📍 回到默认位置</button>
<button @click="getCurrentPosition">🎯 我的位置</button>
</div>
</div>
</div>
</template>
<style scoped>
.map-page {
flex: 1;
display: flex;
flex-direction: column;
min-height: 0;
overflow: hidden;
}
.map-container {
flex: 1;
min-height: 0;
}
.map-controls {
flex-shrink: 0;
/* ... */
}
</style>
```
**问题:** 页面出现了垂直滚动条,地图也没有正确渲染。
---
## 考察知识点
| 层级 | 知识点 | 具体体现 |
|------|--------|----------|
| **CSS 盒模型** | `height: 100%` 的继承链 | 百分比高度必须逐级传递,任意一环缺失都会断裂 |
| **Flexbox** | `flex: 1``min-height: 0` | flex 子项默认 `min-height: auto`,可能导致溢出 |
| **溢出控制** | `overflow` 的层级管理 | 在哪一层阻止溢出、哪一层允许滚动需要精心设计 |
| **第三方库适配** | 地图 SDK 的容器约束 | AMap 要求容器有明确像素高度flex 分配的隐式高度可能不生效 |
| **微前端** | 子应用的挂载点与视口 | `100vh` vs `100%` 在不同环境下的差异 |
---
## 期望答案要点
### 1. 修复百分比高度继承链(关键)
```
初始html → body → #root无高度→ #container100vh ❌ 链断裂
修复html → body → #root → #container全部 100% ✅ 完整链路
```
```css
html, body {
margin: 0;
padding: 0;
height: 100%;
overflow: hidden; /* 根级截断,杜绝页面级滚动条 */
}
#microapp-vue3-root {
height: 100%; /* 关键:挂载点也需撑满 */
}
#microapp-vue3-container {
height: 100%; /* 用 % 而非 vh适配微前端环境 */
overflow: hidden;
}
```
**为什么 `100vh` 不够好?**
- 移动端浏览器地址栏收起/展开会改变 `100vh` 的实际值
- 微前端环境中,子应用容器不一定等于视口高度,`100%` 跟随父容器更稳健
### 2. flex 布局的溢出收缩
```css
.sub-app-main {
flex: 1;
min-height: 0; /* 允许收缩到内容以下 */
display: flex;
flex-direction: column;
}
.map-page {
flex: 1;
min-height: 0; /* 同上 */
overflow: hidden; /* 阻止地图页面自身溢出 */
}
```
**关键原理:** flex item 默认 `min-height: auto`,不允许收缩到内容高度以下;设为 `0` 后才真正服从 flex 分配。
### 3. AMap 容器必须有明确的尺寸
```css
.map-container {
flex: 1;
width: 100%;
min-height: 0;
}
/* 避免display: none 导致容器尺寸为 0 */
.map-hidden {
visibility: hidden; /* ✅ 保留占位,地图可正常初始化 */
/* display: none; */ /* ❌ 尺寸归零,地图无法渲染 */
}
```
配合 JS 端延迟 `resize`
```js
onMounted(async () => {
const map = await initMap()
if (map) {
nextTick(() => map.resize()) // 修正 flex 布局下的初始尺寸
}
})
```
### 4. 不同页面的滚动策略分离
地图页不需要滚动,但首页/关于页可能需要:
```css
.sub-app-main {
overflow-y: auto; /* 允许滚动 */
}
.map-page {
overflow: hidden; /* 地图页自己阻止溢出 */
/* flex: 1 意味着它恰好填满父容器,不会触发父级的 overflow-y */
}
```
---
## 加分项
- 能说出 `display: none` vs `visibility: hidden` 对第三方库 DOM 测量的影响
- 能解释为什么在 `onMounted` 中调用 `map.resize()`,而不是 `onBeforeMount`
- 能分析 `resizeEnable: true` 的 AMap 配置与手动 `resize()` 的配合关系
- 能指出微前端场景下 `body { margin: 0 }` 不生效的边界情况(宿主页面的 body 不由子应用控制)
---
## 追问方向
1. **如果不用 flex还有什么方案** → grid、absolute + calc、vh 运算
2. **如何保证在其他页面(首页/关于页)的滚动正常?** → 仅在父级 `.sub-app-main``overflow-y: auto`,子页面不设 `overflow: hidden`
3. **`min-height: 0` 为什么是必须的?** → flex item 的隐含 `min-height: auto` 阻止收缩
4. **如果地图加载失败,当前布局会崩吗?** → 状态提示用绝对定位浮层,不占 flex 空间,不影响布局