UI方向前端面试复习地图:从盒模型到合成层的核心考点
这几年我前前后后面过的候选人没有几百也有上百了其中一个很有意思的现象是一听到“UI 相关”四个字不少人立刻放松警惕觉得无非是问几个 CSS 属性、看看你写的页面好不好看。可真到了现场被问到层叠上下文、合成层、设计稿还原闭环一下子就露怯了。现在包括 Gemini 在内的 AI 工具确实提高了大家背题的速度但如果只会让 AI 给答案没有把答案组织成自己的理解一到变种追问还是容易翻车。UI 方向的前端面试题其实远比很多人想的要深它对面的是你对浏览器渲染、布局算法、性能优化和工程协作的综合理解。这篇文章把我在面试中常问的 UI 方向题目整理成一张复习地图按基础知识、布局与设计、项目经验与优化、进阶知识四个维度展开每个题目都给到可以直接拿去用的回答思路和踩坑经验。1. 基础题别背概念盒模型、层叠上下文和语义化的真实考点1.1 盒模型border-box 怎么用才能用对——并不只是全局 reset 一句话面试问“说说标准盒模型和怪异盒模型”表面考概念实际想看你是不是真的在项目里被宽度计算坑过。标准盒模型content-box里width 只表示内容区域宽度padding 和 border 是额外叠加在 width 外面的怪异盒模型border-box里width 是内容 padding border 的总宽度。差别看起来就一句话但放到多列布局里体感完全不同。我举个例子一个 400px 的容器要分成三列每列之间有 20px 间距同时每列内部还要有 20px 的 padding。用 content-box 的话你得做400 / 3再减间距、再减 padding 的连环计算中间还得考虑 border 的 1px 或 2px稍微改一个值整行就换行。用 border-box 以后宽度设成百分比就是视觉宽度的百分比padding 和 border 向内挤压几个列的宽度加起来永远等于容器宽度改起来省心太多。所以我的常规做法是在全局 reset 样式里统一设置*, *::before, *::after { box-sizing: border-box; }这段代码很多人都会写但面试时值得多说两层第一不是所有元素都理应使用 border-box跟第三方全局样式冲突时需要针对容器单独覆盖第二真要吃透盒模型不能只背 width 算法还要知道box-sizing的继承性问题建议在 reset 里显式设置而不是靠content-box默认值。能讲出“我会用 border-box 作为默认但它不解决所有宽度认知问题”的人和只会抄 reset 的人一听就不一样。1.2 层叠上下文为什么 z-index: 99999 还是压不住弹窗“我设置了 z-index: 99999为什么弹窗还是被遮住”这道题我特别爱问。很多人第一反应是“你 z-index 还不够大”但实际上 z-index 只在同一个层叠上下文内部比较大小。如果一个元素处于某个父级层叠上下文中父级的堆叠层级是 1另一个兄弟容器的堆叠层级是 2那无论你把子元素的 z-index 设到多大它都被锁在父级那一层里。这就是所谓的“赢者通吃”。触发层叠上下文的属性远不止 z-index 一个。position 值为 relative/absolute/fixed/sticky 且 z-index 不为 auto、flex/grid 子项目且 z-index 不为 auto、opacity 小于 1、transform 非 none、filter 非 none、will-change 指定了这些属性还有 backdrop-filter都会创建层叠上下文。这个知识点特别容易踩坑尤其在做弹窗、下拉、抽屉这类固定定位组件的时候。我真实排过的一个案例是这样项目里有嵌套弹窗子弹窗总被蒙层盖住。看 DOM 结构没问题蒙层在子弹窗后面子弹窗 z-index 也设置了很大的值理论不该被遮。打开 DevTools 一查发现子弹窗的父级是一个用了 transform 属性做入场动画的包裹元素父级已经形成了一个层叠上下文整体堆叠层级低于蒙层。所以子弹窗内部 z-index 再大只能在父级上下文里横跳。解决办法是把弹窗组件从动画容器中移出或者让蒙层也挂到同一层叠上下文中。答这道题时能说清楚“层叠上下文是树形结构z-index 是局部比较”再顺手提一下 will-change 和动画的关联基本就是满分回答。1.3 语义化和可访问性UI 开发为什么要在意“一个按钮用 button 还是 div”很多前端觉得语义化是 SEO 的事写 UI 时 div 一把梭最后靠 role 和 tabindex 圆回来。这种思路在面试里很容易暴露。语义化首先是信息的结构化表达浏览器、爬虫、读屏软件都需要通过标签结构来理解页面。header、nav、main、article、aside、footer 这些标签给内容赋予角色也让 CSS 选择器和 JS 事件代理有了更可靠的钩子。但对 UI 开发来说最有价值的语义化讨论是“交互控件该用什么标签”。一个页面上的“点赞”按钮到底是 button 还是 a如果点击后只是触发前端逻辑就应该是 button如果点击后会跳转到新的 URL允许用户在新标签页打开就应该是 a。button 自带键盘访问和回车/Space 触发行为a 支持链接语义和新窗口打开。区分它们不是为了遵守规范而是为了避免真实浏览器里的行为错乱。面到可访问性时我一般还会追问焦点管理和 aria。做弹窗、抽屉、Toast 这类组件时打开后焦点应该移到弹窗内部关闭后焦点应该回到触发元素遮罩、进度提示等非文本信息需要有文本替代。UI 开发经常被误解为只是视觉实现但真正专业的 UI 工程师会把焦点管理和读屏适配纳入验收标准面试时主动说出这一点会明显区别于只会切图的候选人。2. 布局题看功力Flex 与 Grid 的选型逻辑和设计稿还原闭环2.1 flex: 1 到底展开成什么——flex-basis 是很多人没想透的点“flex: 1 等价于什么flex: auto 和 flex: 1 有什么不同”这是布局题里出现率极高的一道。flex: 1 是flex-grow: 1、flex-shrink: 1、flex-basis: 0%的缩写。很多同学知道 flex: 1 可以让子项等分剩余空间但追问到 flex-basis 0% 和 auto 的区别就开始卡壳。flex-basis 决定了项目在空间分配前的初始主轴尺寸。auto 表示取项目自身的宽高属性或内容尺寸0% 表示初始尺寸为零空间全部由 grow/shrink 决定。所以 flex: 1basis: 0%的行为是“所有子项从同一起跑线瓜分容器空间”而 flex: autobasis: auto则是“先按各自内容尺寸排布再分配剩余空间”。实际场景中图片加文字混合的列表用 flex: auto 更容易保持图片原始宽度不被过度挤压而等分导航、标签栏这类需求用 flex: 1 才精确。还有一个高频附加考点是min-width: 0。flex 子项默认min-width: auto内容最小宽度不能被压缩超长文本或长 URL 会把 flex 容器撑破。给子项设min-width: 0后项目才能真的被压缩到比内容更窄。处理表格单元格、英文单词换行时尤其常见可以说这是 flex 布局里最隐蔽的 Bug。面试时如果能举出一个真实列表结构现场说“这里我用 flex: 1 而不是 flex: auto是因为需要严格等分同时给子项补了 min-width: 0 防止长文本溢出”比单纯背缩写含义有效太多。2.2 Grid 和 Flex 的选型先分清一维和二维再说谁取代谁“你是如何选择 Flex 还是 Grid 的Grid 会不会取代 Flex”答案是不存在谁取代谁。Flex 擅长一维布局关注单个方向上的分配与对齐Grid 擅长二维布局可以同时控制行和列。一个常见误区是看到“看起来像个网格”就上 Grid其实很多只是简单的横向排列用 flex-wrap 更顺手。我的选型判断大致是这样顶部导航加左侧菜单加右侧内容区这种典型后台布局用 Grid 定义整体骨架很舒服几个区域用grid-template-columns和grid-template-rows一写比三层嵌套 flex 干净很多而一组按钮、标签、卡片内部的排列Flex 足够且心智负担小。换句话说Grid 负责页面宏观布局Flex 负责组件内部微观排列。用表格整理的话大概是这样的逻辑场景推荐方案理由页面整体骨架、复杂二维区域Grid同时控制行与列代码更简洁组件内部水平/垂直排列Flex一维分配逻辑简单直观内容数量不定、需要自动换行Flex flex-wrap更适合流式内容有明确行列的卡片矩阵Grid行列对齐天然成立还有一种问法是让你实现 12 栅格系统用 Grid 可以这么写.grid-12 { display: grid; grid-template-columns: repeat(12, 1fr); gap: 16px; } .col-4 { grid-column: span 4; }这比用 float 或 flex 做栅格简单很多gap 还天然支持间距而不需要每个子项单独写 margin。但要注意 gap 会吃掉栅格的实际总宽设计稿算宽度时要把间距考虑进去。面试时能主动说出这个细节说明你真的在项目里写过栅格系统而不是临时背代码。2.3 设计稿还原375 设计稿到多端适配的像素级方法“拿到一个 375px 宽的设计稿你怎么在不同宽度设备上还原”这类题已经把范围圈定在移动端适配但面试官想听的绝不是“用 rem”三个字。适配方案要分场景说清楚。基础是 viewport meta 标签没有它整个适配都无从谈起。然后是单位选择流式布局中百分比和 vw 适合宽度类属性文字大小和间距用 rem或者直接用 px 配合媒体查询新的clamp()可以一个属性内同时实现下限值、期望值和上限值例如.title { font-size: clamp(28px, 5vw, 40px); }这句话的意思是最小 28px最大 40px中间按视口宽度 5% 线性变化。面试时提到 clamp() 远比只说 rem 显得跟得上实践。像素级还原还有一些高频细节。1px 边框在 Retina 屏幕上会显得过粗常规做法是借助transform: scale(0.5)缩小背景图出现模糊时先检查设计稿的 2x/3x 切片是否加载页面字体出现锯齿时可能和系统字体栈、抗锯齿属性有关。回答时不要一次把方法全堆出来而是说“我会先定适配基准再单独处理 1px、大屏上限这类边界问题”这样面试官会觉得你有完整方案而不是零散知识点的堆叠。3. 项目题考复盘首屏优化、UI 卡顿定位与组件设计的回答框架3.1 首屏优化从 UI 侧能落地的指标和手段“项目首屏加载很慢从 UI 角度你能做什么”这题进入项目经验面面试官不满足于背优化名词希望听到“你基于什么数据做了哪些改动效果如何”。UI 和首屏最相关的部分是图片、字体、CSS 体积和首屏渲染。图片懒加载是见效最快的把首屏之外的 img 换成loadinglazy再给真实图片区域设置宽高或 aspect-ratio 占位避免布局偏移。字体方面中文字体文件动辄几 MB如果只展示少量数字和英文可以用font-display: swap配合字体子集化方案把大字体拆成常用字符集。骨架屏也是一个 UI 侧非常常见的优化手段价值不只是“好看”而是通过占位结构让用户感知加载进度降低等待焦虑。实现时可以用纯 CSS 动画画灰色块也可以在构建期生成和页面结构一致的图片。搭配异步组件或路由级代码分割时骨架屏能很好地承接加载态。回答这类题尽量把指标带上。LCP最大内容绘制和 CLS布局偏移这两个指标和 UI 强相关。图片不设宽高、字体加载导致文字跳动、异步内容插入都会让 CLS 变高。一个标准的回答句式是“当时我通过 Lighthouse 测出首屏 CLS 在 0.4 左右后来给所有图片设置了 aspect-ratio给字体加了 fallback 和 swap优化后降到 0.1 以下。”有数据有手段有效果这才是项目经验题该有的答法。3.2 UI 卡顿排查用 Performance 面板把“卡”变成数据“页面滚动很卡或者切 Tab 卡顿你怎么定位和解决”这是非常贴近实战的一道题。前端侧的卡顿几乎最终都会落到主线程任务过重。标准步骤是打开 DevTools Performance 面板录制一段操作观察主线程的长时间任务Long Task、样式重计算、绘制和合成事件。凡是耗时超过 50ms 的任务基本就能构成一次肉眼可见的卡顿。这套思路不只适合 Web做 C# 桌面端或者 Unity UI 刷新卡顿排查时也一样本质都是找主线程阻塞源。常见原因之一是强制同步布局代码经常长这样const height el.offsetHeight; el.style.height height * 2 px;第一句读取布局信息第二句修改样式如果中间夹着大量 DOM浏览器可能在读取时触发一次布局计算而你在循环里反复做这件事性能会急剧恶化。正确做法是先把一次性需要的读取收集起来再批量写入或者使用 requestAnimationFrame 分帧处理这就是常说的“读写分离”。我自己排过的一个真实案例是数据表格每一行都绑定了动态进度条动画滚动时肉眼可见的掉帧。用 Performance 录制后发现每次滚动都触发整列合计行的强制重算而且表格在一个很大的 transform 容器里。后来的优化包括表格改成虚拟滚动只渲染可视区域的行进度条动画移到合成层执行关闭不必要的行 hover 阴影。三步做完从“明显卡顿”变成“完全顺手”。面试能把这个链路讲清楚比别人背十个优化名词有效得多。3.3 组件设计题一个 Button 如何引出工程化全貌“如果让你设计一个 UI 库的 Button 组件你会考虑哪些方面”这道题考的不是你会不会写按钮而是有没有用工程化思维做组件。先从 API 设计说起type、size、disabled、loading、htmlType、icon 这些属性怎么命名要不要区分点击事件和原生事件。props 的命名会影响使用体验也影响后续文档生成。然后是样式体系颜色、圆角、间距、字体应该来自设计令牌而不是在组件里硬编码。这里有个实际权衡CSS Variables 适合运行时切换主题Sass/Less 变量适合构建期处理要支持动态换肤根节点上挂--primary-color这类变量是常见做法。组件还需要考虑状态与可访问性loading 时要不要禁用禁用状态下焦点怎么处理加载中的按钮 icon 是否需要 aria-hidden点击是否带来重复提交风险这些细节往往能分出普通开发者和能独立负责组件库的人。最后可以提按需加载组件样式如何实现按需引用是否支持 tree-shaking包体积控制在什么量级。一道 Button 题表面不难但能把 API、设计令牌、无障碍、构建优化串起来的人非常少。面试时按这个结构答对方会明显感觉到你不只是“会写页面”。4. 进阶题拼原理渲染流程、合成层和团队 UI 规范的底层脉络4.1 渲染流程题HTML、CSS 和 JS 的阻塞关系与关键渲染路径“浏览器从拿到 HTML 到显示页面经历了哪些步骤CSS 和 JS 分别有什么阻塞影响”这道题是进阶面几乎必考的。整个链路是HTML 解析生成 DOM 树CSS 解析生成 CSSOM 树二者合并成渲染树随后进行布局、分层、绘制和合成。布局关心元素的位置和尺寸绘制关心像素填充合成关心把不同的层叠加到屏幕上。阻塞关系是另一个重点。CSS 是渲染阻塞资源浏览器在构建出完整 CSSOM 之前不会渲染所以 CSS 要尽量放在 head 里并做压缩合并普通 script 标签会阻塞 HTML 解析因为脚本可能修改 DOM。async 和 defer 的关键区别在于defer 按文档顺序执行且等 DOM 解析完再执行async 下载完立即执行可能阻塞解析多个 async 脚本的顺序也不保证。能把这些资源加载行为讲清楚说明你对性能有系统认识而不仅仅是背了几个属性。加分表述是重排和重绘的成本差异以及为什么 transform 能跳过重排重绘。重排的成本远高于重绘重绘又比合成高得多。所以优化的总体原则是尽量只触发合成层变化不触碰布局和绘制。答到这里你已经从 CSS 属性层面上升到渲染原理层面这类人正是 UI 方向进阶面想筛选出来的。4.2 动画性能题为什么 transform opacity 能跑满 60fps“实现一个流畅的位移动画你会怎么写为什么推荐 transform 而不是 left/top”动画流畅度取决于主线程是否被布局和绘制拖住。用 left/top 改变位置时浏览器每帧都要重新计算布局、触发绘制主线程很容易在移动端上崩帧。而 transform 可以触发合成动画在合成线程上执行主线程负担小自然更接近 60fps。比较规范的写法是element.animate( [ { transform: translateX(0) }, { transform: translateX(200px) }, ], { duration: 400, easing: ease-in-out } );或者用声明式 CSS.box { transition: transform 0.4s ease; }如果面试只背出“用 transform”还不够最好能解释 will-change。给元素设置will-change: transform是提前告知浏览器然后浏览器把元素提升到独立合成层减少动画开始时的首次提升耗时。但注意别滥用每个独立合成层都会占用内存图层数量过多同样会导致性能下降。移动端上给几十个元素设置 will-change内存可能先崩。实用的做法是只在动画期间设置结束后移除。能把这个滥用风险说出来面试官会确认你是真的调过动画性能而不是背书。4.3 团队规范题如何推动 UI 规范落地而不是变成“只说不动”“你们团队在做 UI 规范但大家不执行你作为前端怎么推动”这类情境题考的是协作能力和系统思维。我的建议是先别急着写规范文档。很多团队规范文档写得非常完善但没人看因为距离实际开发太远。推动规范落地最好的方式是把规范沉淀成工具链和组件库让正确做法成为默认选项。比如视觉间距这套与其在文档里写“8px 基准间距”不如设计一组 spacing 变量并在样式代码中强制使用。另一个可落地的点是 lint 和格式化。Stylelint 可以检查颜色、单位、命名规则比如禁止魔法值颜色要求使用设计变量。CSS Modules / scoped / CSS-in-JS 的选型也属于工程规范Vue 项目通常 scoped 够用组件库或需要动态主题时CSS 变量加 CSS-in-JS 各有优势。选型的理由要能说清楚而不是哪个火用哪个。视觉走查也是团队规范的一部分。UI 开发完成后由设计或前端负责人对关键页面做一轮像素级检查问题通过 issue 记录避免口头沟通过后就淹没在聊天记录里。写了规范和让规范真正长在流程里是两件不同的事。面试时强调后者会给面试官留下务实的印象。5. 面试现场策略同样一道题怎么答出面试官想听的差异感5.1 用 STAR 框架讲“最复杂的 UI 需求”别把复盘讲成流水账“你做过最复杂的 UI 需求是什么”很多候选人从项目背景开始讲半小时最后面试官只记住“这个项目很复杂”。想讲好这道题先用 STAR 结构搭骨架背景、目标、行动、结果。背景部分一两句话交代即可重点放在行动和结果。举一个框架示例背景是后台系统需要支持多角色、多权限的动态菜单和配置页面任务是把权限数据和 UI 结构解耦让非前端也能配置页面行动是你做了路由配置映射、动态渲染组件、建立权限指令与按钮级控制并处理了异步权限与页面缓存的冲突结果是配置周期从平均 3 天降到 2 小时UI 相关 Bug 数量下降 60%。这里的关键是数字最好是你真实做过并能应对追问的。不要记流水账的另一个技巧是始终围绕“UI/前端”展开。哪怕项目整体架构很有亮点也要说明你的具体工作在哪部分避免让面试官觉得你只是全程参与但说不清自己的贡献。5.2 被追问到盲区时的三层缓冲知道、推测、求证“如果面试官问到一个你确实不知道的问题怎么办”这个问题本身几乎必被问到但实际上考察的是现场行为。第一层把你已经知道的相关知识快速组织出来。比如问 GPU 合成原理你不知道细节但你知道合成发生在主线程外知道 Layer 和合成树先把这部分说出来。第二层基于已知做推断并明确说明“这是我基于已知知识的推测”。比如猜 transform 会触发独立合成层所以动画不占用主线程这个推断基本正确。第三层坦诚未知并当场向面试官请教实际场景里的做法或者提出“我会回去查一下用一个小 Demo 验证后补充答案”。这个策略的价值在于它把“我不会”变成了“我能用已有知识逼近答案而且知道如何获取正确信息”。面试官很少要求候选人是百科全书但很看重遇到未知问题时的反应和思路。UI 领域尤其如此因为 CSS 诡异行为太多谁都不可能全部记牢能快速定位并验证才是核心能力。面试时把这个方法自然地用出来远比支支吾吾或者硬编一个答案体面得多。我自己面试这么多人的一个体会是很多看起来基础的 UI 面试题恰恰是区分层级的试金石。同样问盒模型初级背定义中级说用法高级会扯到布局策略和标准化配置。准备这些题最好的方式不是刷题而是找自己项目里真实踩过的 UI 问题把根因、排查过程和结果写成两三段文字面试时用自己的话讲出来。这样的回答天然带着你个人的技术判断和背来的答案一对比就高下立判。如果你正在准备面试不妨先把上面的问题挨个过一遍每个都想着怎么跟自己的项目经验结合练上几天效果会很不一样。