高德地图添加轨迹规划和重放功能
This commit is contained in:
242
docs/interview-question-map-layout.md
Normal file
242
docs/interview-question-map-layout.md
Normal file
@@ -0,0 +1,242 @@
|
||||
# 前端面试题:全屏三区布局(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(无高度)→ #container(100vh) ❌ 链断裂
|
||||
修复: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 空间,不影响布局
|
||||
Reference in New Issue
Block a user