拓冰建站拓冰建站
首页 / 资讯中心 / 正文

CSS面试必考:Flex/Grid布局、层叠上下文与移动端适配全解析

写这篇之前先说明一下这是“CSS面试必考八股文”系列的第二篇。上一篇把CSS选择器、盒模型、层叠规则、BFC这些底层概念过了一遍这次接着聊更容易在面试中被追问、也更容易暴露水平的几块内容Flex/Grid布局原理、定位与层叠上下文、动画与性能以及移动端那些坑。如果你正在准备前端面试或者带人复盘基础这篇应该能帮你省不少时间。1. Flex布局面试里最常被追问的细节1.1 flex-grow、flex-shrink、flex-basis的分配逻辑Flex布局是面试必问但大部分候选人只背了“水平垂直居中”那一套。真正拉开差距的是问他 flex-grow / flex-shrink / flex-basis 这三个属性的分配逻辑。先说 flex-basis。它决定了项目在主轴上的初始尺寸优先级高于 width在没有被 flex-grow/shrink 影响的情况下。默认值是 auto意思是“看项目自身的 width 属性”如果设置成具体数值比如 flex-basis: 200px那项目的主轴尺寸就是 200px。关键考点是flex-basis 设置的是 content-box 还是 border-box答案取决于 box-sizing面试官问到这里基本是在考察你对盒模型的理解是否贯通。然后是 flex-grow它解决的是“空间有剩余时怎么分配”。假设容器主轴剩余空间是 300px三个项目 flex-grow 分别是 1、2、3那剩余空间会按比例 1:2:3 分配也就是 50px、100px、150px。这里有个细节flex-grow 分配的是剩余空间不是最终尺寸的比例。很多候选人搞混这一点会答成“按自身宽度的比例放大”这是错的。flex-shrink 则是“空间不足时怎么压缩”。容器总宽 600px三个项目 flex-basis 都是 300px总 900px溢出 300px。如果 flex-shrink 都是默认值 1那每个项目都压缩 100px最终每个 200px。但如果某个项目设置 flex-shrink: 0它就不会被压缩其他两个项目要承担全部 300px 的压缩量各变成 150px。面试加分回答flex-shrink 的压缩不是简单按 shrink 值比例均摊而是“先按 flex-shrink 比例、再乘以其 flex-basis 权重”来计算。场景是各项目 flex-basis 不同较大的项目会被压缩更多。这里如果能把公式讲出来我一般直接让他过。1.2 主轴与交叉轴justify-content和align-items的易混点另一个高频考点是主轴main axis和交叉轴cross axis的区分。flex-direction 默认是 row主轴水平从左到右改成 column 后主轴变成垂直从上到下。justify-content 永远作用于主轴align-items 作用于交叉轴这个别搞反。实际面试中一种常见问法是flex-direction: column 时如何实现水平居中不少候选人下意识回答“justify-content: center”然后被面试官反问“主轴不是垂直了吗”才反应过来应该是 align-items: center。这就是对主轴变换不敏感。还有一个进阶坑align-content 和 align-items 的区别。align-content 只在“多行/多列”时生效它控制的是整个 flex 行集合在交叉轴上的分布方式align-items 控制的是单行内各项目在交叉轴上的对齐方式。如果容器只有一行align-content 不起作用。这个点能讲清楚说明你是真用过不是背的。1.3 面试加分flex:1到底等于什么很多前端常用 flex: 1 来实现“剩余空间占满”但问他 flex: 1 实际展开成什么经常答不上来。答案是 flex: 1 1 0%即 flex-grow: 1、flex-shrink: 1、flex-basis: 0%。这里有个关键区别flex-basis: 0% 和 flex-basis: auto 在分配空间时的行为完全不同。base 是 0%则所有项目从 0 开始按 grow 比例分配容器宽度比如三个项目都 flex: 1则三等分如果 base 是 auto项目会先按内容宽度占据空间剩余空间再按 grow 分配导致内容多的项目最终更宽。这种细节笔试中出现过很多次值得留意。常见的简写对照简写展开实际效果flex: 1flex: 1 1 0%等分剩余空间按比例伸缩flex: autoflex: 1 1 auto按内容尺寸为基础再分配剩余空间flex: noneflex: 0 0 auto不伸缩保持内容尺寸flex: 0 1 auto默认值可收缩但不可放大实操心得移动端适配里底部导航栏的中间按钮经常需要“绝对居中但两侧空间不相等”用 flex: 1 justify-content 不好处理时我习惯给左右各一个 flex: 1 的空元素占位再给中间按钮固定尺寸简单可靠比 position 计算偏移量省事太多。2. Grid布局从“能看懂”到“能讲明白”2.1 核心属性速记容器与项目的分工CSS Grid 在面试中出现的频率这几年明显上升尤其是涉及复杂页面布局的岗位。Grid 的核心考点在于“容器属性”和“项目属性”的分工。容器属性包括grid-template-columns / grid-template-rows划分行列、grid-template-areas区域命名、grid-gap间距、justify-items / align-items单元格内对齐、justify-content / align-content网格整体对齐。项目属性包括grid-column / grid-row合并与定位、grid-area区域简写、justify-self / align-self自身对齐。面试官经常问一个类比题“Grid 和 Flex 的区别是什么”比较准确的回答是Flex 是一维布局模型适合处理“一行或一列”内的对齐与分配Grid 是二维布局模型同时管理行和列。Flex 关注内容沿主轴的流动Grid 关注网格单元格的放置。实际操作中两者常结合用——页面骨架用 Grid组件内部用 Flex。2.2 fr单位与auto的差别grid-template-columns: 1fr 1fr 和 grid-template-columns: auto auto 在实际呈现上差别很大。fr 是“剩余空间份额”会把容器可用空间扣除固定尺寸后按比例分配auto 是“内容自适应”列宽由该列最宽项目的内容决定。举个例子容器宽 900pxgrid-template-columns: 200px 1fr 2fr那么第一列固定 200px剩下 700px 按 1:2 分配给第二、三列即约 233px 和 467px。如果是 auto 1frauto 列会被内容撑开剩余的再给 1fr。这个区别在响应式布局中很常用比如侧边栏固定宽度、主内容自适应就可以用 grid-template-columns: 240px 1fr。还有 minmax() 函数它是 Grid 里非常经典的考点。grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)) 可以实现“列数自适应”的卡片布局——容器宽就多排几列窄就少排几列但每列不小于 200px。这个写法的好处是无需媒体查询就能完成一定程度的响应式值得在项目中实践。2.3 实战中的Grid场景场景一经典双栏布局左侧 240px 固定右侧自适应。用 Grid 就是一行代码grid-template-columns: 240px 1fr。场景二圣杯布局header 三列 footerFlex 时代要套三层容器Grid 时代一行搞定.layout { display: grid; grid-template-columns: 200px 1fr 200px; grid-template-areas: header header header left main right footer footer footer; grid-template-rows: auto 1fr auto; min-height: 100vh; }配合 grid-area: header 等命名代码可读性直接上升一个档次。面试时如果能主动写出 grid-template-areas 方案会比只会 flex 的候选人更有亮点。场景三商品卡片网格图、标题、价格、按钮左对齐用 Grid 的 align-items 控制每行高度一致同时项目内部用 flex-direction: column 让底部按钮始终贴底。Grid 管“外部对齐”Flex 管“内部排布”两者配合是真实项目中最常用的组合。注意Grid 的兼容性在 2024 年之后基本无需担心现代浏览器全面支持。但如果你维护的是老项目比如嵌在低版本 WebView 里建议先用 display: grid 做个基础降级再配合 supports 做增强不要一上来就全量替换。3. 定位与层叠看似简单实则高频翻车3.1 relative、absolute、fixed的细节考点定位相关的面试题表面简单但陷阱很多。面试官最爱问relative 的定位参考点是什么答案是“元素自身原本的位置”absolute 的定位参考点是什么是“最近的已定位祖先position 不为 static”如果找不到就是初始包含块通常是视口。这个“最近的已定位祖先”就是关键考点。还有一个细节absolute 元素如果祖先设置了 transform 属性即使它的 position 是 static也依然会成为 absolute 的定位参考。因为 transform 会生成“包含块”。这是很多候选人答不出来的点也是开发中遇到“明明没有定位祖先元素却跑了”的常见原因。fixed 和 absolute 的区别也要能讲清楚fixed 默认相对于视口定位但如果祖先有 transform、filter、perspective 等属性fixed 会变成相对于该祖先定位。这个特性在“弹窗被裁剪”的问题中是核心原因。面试如果聊到弹窗组件一定要主动提到给弹窗的 DOM 树里任何一个父节点加 transform都会破坏 fixed 的视口定位。3.2 sticky的“粘性定位”有哪些隐藏陷阱position: sticky 是新面试题里的常客。它结合了 relative 和 fixed 的特点元素在滚动过程中先按正常文档流排列到达指定阈值后“粘”在视口的某个位置。常见写法是 top: 0表示元素顶部距离视口顶部 0 时开始吸顶。但 sticky 有非常多的触发条件稍不注意就不生效。最核心的父容器高度不够、或父容器 overflow: hidden / auto / scroll都会导致 sticky 失效。原理是 sticky 的作用范围被限制在父元素内父元素 overflow 不为 visible 时会建立一个“滚动上下文”sticky 在这个上下文内无法相对视口计算位置。面试时回答出“sticky 的偏移计算受最近滚动祖先影响”这个层面基本就能过关。另一个实操建议吸顶头部和吸底按钮组合时sticky 会比 fixed 更合适。因为 sticky 元素不影响文档流不需要给内容区额外加 padding 来补偿头部高度滚动到页面底部时按钮也不会遮挡内容。3.3 z-index和层叠上下文为什么设置了z-index还是不生效z-index 不是越大越靠前这个错误认知在面试和工作中都很常见。z-index 只在“同一层叠上下文”内比较大小。如果两个元素分别位于不同的层叠上下文中它们的 z-index 数值大小没有直接可比性实际层级由祖先层叠上下文的层级决定。哪些情况会创建层叠上下文常见的有position 非 static 且 z-index 不是 autoopacity 小于 1transform 非 nonefilter 非 nonewill-change 指定了上述属性flex/grid 子项且 z-index 非 autodisplay: flow-root。所以“z-index: 99999 还是被盖住”的排查思路应该是沿 DOM 树往上找看元素的父级、祖父级是否创建了层叠上下文然后把 z-index 往上层加而不是无限放大当前元素。这里是“八股文”和“实战能力”的分水岭。独家技巧排查层叠问题时我一般在 DevTools 里给目标元素临时加一个 outline: 3px solid red确认它渲染在了正确位置再用“元素面板 - 层叠上下文树”来看各层级的包含关系。Chrome 的 Layers 面板特别适合这类问题。4. 动画与transform视觉题也能问出深度4.1 transform中rotate3d、translateZ等考点CSS 动画面试题现在越来越偏向视觉还原类。rotate3d 是其中一个典型考点。rotate3d(x, y, z, angle) 的参数含义是绕一个三维向量旋转。比如 rotate3d(1, 0, 0, 45deg) 是绕 X 轴旋转 45 度元素像翻书页一样翻转rotate3d(0, 1, 0, 45deg) 绕 Y 轴旋转像旋转门rotate3d(0, 0, 1, 45deg) 等价于 rotate(45deg)绕 Z 轴平面旋转。transform-origin 是另一个考点它决定变换的基准点。默认是 50% 50%即元素中心。如果做“开关门”效果应将 transform-origin 设置为左边或右边比如 left center否则看起来像中心旋转而不是门轴旋转。深一层的问题是为什么 transform: translateZ(0) 能触发 GPU 加速原因是它把元素提升为一个合成层composited layer由 GPU 直接合成避免重新触发布局和绘制。面试回答到这里顺带提一句“但不要滥用否则合成层过多反而导致内存压力”会显得你有真实项目经验。4.2 动画性能为什么用transform/opacity而不是left/top这是前端性能面试中几乎必问的考点。在动画过程中修改 left/top 会触发布局layout和绘制paint每一帧都要重新计算几何信息性能开销很大而修改 transform 和 opacity 只触发合成composite由 GPU 处理开销小得多。背后的原理是浏览器渲染流水线JavaScript - Style - Layout - Paint - Composite。如果动画修改 left从 Style 阶段开始就要全面重算 Layout一帧内容变化大如果修改 transform浏览器可以直接在合成阶段复用之前的纹理做位移动画开销接近“免费”。实操上的建议是能用 transform 实现的动画就不用 left/top能用 opacity 做的淡入淡出就别用 display: none 硬切。另外 will-change: transform 可以提前告诉浏览器“这个元素将要变换”让它提前建合成层但它不是万能的用得太多反而会消耗内存建议只对动画中的元素使用。4.3 字体渐变、涟漪光圈等视觉效果的实现原理字体渐变在热搜词里出现频率很高。实现方式其实不复杂核心是利用 background-clip: text 把背景裁切到文字形状上再配一个渐变背景.gradient-text { background: linear-gradient(90deg, #ff6a00, #ee0979); -webkit-background-clip: text; background-clip: text; color: transparent; }这里有个值得在面试中提一句的点color 必须设为 transparent不然文字默认颜色会盖住背景。另外 background-clip: text 的浏览器兼容性已经很好但某些 WebView 上仍需要 -webkit- 前缀。涟漪光圈扩散效果ripple的实现思路是一个圆形元素不断放大并降低透明度。核心是利用 transform: scale 和 opacity 的动画组合实现生成多个层叠的波纹时每个波纹用不同的 animation-delay 错开时间效果就能连续起来。这类“视觉题”的考点往往不是效果本身而是你能不能拆解成 transform/opacity 的合理组合并回答出性能差异。5. 移动端与设备兼容面试官最爱问的工程化问题5.1 1px问题的成因与几种解法移动端 1px 问题几乎人手一份答案。成因是设备像素比devicePixelRatio大于 1 时CSS 的 1px 在物理屏幕上实际占据多个物理像素导致线条看起来“偏粗”。常见解法有四种方案一transform: scaleY(0.5)。给元素加伪元素高度为 1px再垂直缩放 0.5.hairline { position: relative; } .hairline::after { content: ; position: absolute; left: 0; top: 0; width: 200%; height: 1px; transform-origin: left top; transform: scaleY(0.5); }方案二使用 0.5px 单位。border: 0.5px solid #eee。但部分安卓机不支持 0.5px。方案三使用线性渐变模拟。background: linear-gradient(to top, #eee 50%, transparent 50%)实际上是绘制半像素高度的渐变视觉上更像细线。方案四postcss-write-svg、postcss-border-1px 等工具方案原理还是 transform 缩放。面试如果提到 1px我建议主动说出方案一和方案四的取舍方案一的伪元素方案最稳兼容性最好工具方案适合批量处理但需要配合构建流程。5.2 苹果底部安全区与CSS的兼容方案热搜词里“小程序苹果底部兼容css”指向的就是 iOS 全面屏底部安全区问题Home Indicator 区域。方案核心是 env(safe-area-inset-bottom)。iOS 11 及以上支持配合 viewport-fitcover 才能生效。在网页中需要设置meta nameviewport contentwidthdevice-width, initial-scale1.0, viewport-fitcover /然后给底部固定元素加.bottom-bar { padding-bottom: env(safe-area-inset-bottom); }如果担心低版本 iOS 不支持 env可以加一层 fallback.bottom-bar { padding-bottom: 15px; padding-bottom: constant(safe-area-inset-bottom); /* iOS 11.0-11.2 */ padding-bottom: env(safe-area-inset-bottom); /* iOS 11.2 */ }小程序里的写法类似但要注意小程序环境对 env 的支持要配合官方提供的兼容方案常见思路是在页面的容器上设置 padding-bottom或者用占位元素撑开底部安全区。另一个容易踩的坑如果页面本身有滚动区域在滚动容器上直接加 padding-bottom 可能无效要在滚动内容的内部容器上加。踩坑记录我遇到过“底部按钮被 Home Indicator 遮挡”的问题当时只设置了 padding-bottom: constant(...)但 Android 端不认 constant导致安卓上底部留白过大。最终方案是配合 supports (padding-bottom: env(safe-area-inset-bottom)) 做特性检测只有支持 environ 的机型才加安全区 padding。5.3 文本位置调整的常见场景与坑热搜词里“怎么调整css容器里的文本位置”其实覆盖了非常多面试题单行文本垂直居中line-height height、多行文本垂直居中flex align-items: center、文本水平居中text-align: center、文字与图标对齐vertical-align: middle 或 flex 布局等。面试里有一个经典陷阱line-height 实现垂直居中对单行文本有效但文本换行后 line-height 仍然生效多行之间行距过大、内容超过容器高度就会出现视觉偏移。此时应该用 flex 或 grid 的对齐属性而不是依赖 line-height。另一个关于 div 底部对齐的问题也很高频。想让元素固定在父容器底部最简单的是 flex margin-top: auto或父级设置 flex-direction: column子级用 margin-top: auto 也可以实现。Grid 方案则是 grid-template-rows: 1fr auto下面那行自动贴底。相对比 position: absolute bottom: 0 更方便的地方在于它不脱离文档流不会遮挡其他内容。6. 高频CSS面试题速查表整理一个表格方便考前快速过一遍问题考察点推荐回答要点flex: 1 展开是什么flex 简写flex: 1 1 0%flex-shrink 的压缩逻辑空间分配按 shrink 比例与 basis 权重综合计算Grid 和 Flex 区别布局模型一维 vs 二维fr 和 auto 的区别Grid 单位fr 是剩余空间份额auto 由内容决定sticky 不生效原因定位限制父容器高度不够或 overflow 非 visiblez-index 失效原因层叠上下文不在同一个层叠上下文内比较transform 为什么比 left 动画流畅渲染流水线只触发合成不触发布局与绘制background-clip: text 的作用视觉效果背景裁切到文字配合 color: transparent1px 问题原因设备像素比dpr 1 导致物理像素不足env(safe-area-inset-bottom)iOS 安全区viewport-fitcover env这张表适合在面试前一小时快速扫一遍。每一条被追问深一层时都能回到前面章节的原理解释里去能答到那个程度CSS 这关基本就稳了。关于 CSS 面试准备我个人的体会是不要只背结论要把每个属性放在“浏览器渲染流程、层叠上下文、布局模型、性能开销”这几条主线上去理解。面试官追问的往往是结论背后的为什么只要能把为什么讲清楚结论本身是不是一字不差反而不太重要。这也是这个系列想要传达的八股文不是背出来的是理解出来的。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门