CSS面试高频考点全梳理:从盒模型到原子化CSS
2026/8/29 22:56:10 网站建设 项目流程

最近帮团队做前端面试,前后筛了几十份简历,发现一个挺有意思的现象:很多候选人项目经历写得花里胡哨,但一问到CSS基础,翻车的比例相当高。不是不会用,而是说不清楚原理。Flex能调布局,却讲不明白flex: 1到底是什么意思;会用position: absolute,但说不清它相对谁定位;能写出display: grid,但问到grid-template-columnsgrid-template-rows配合使用时就开始含糊。这其实就是典型的“八股文”没背透。

做前端这几年,我越来越觉得CSS基础不是背题,而是建立一套“浏览器怎么理解样式”的思维模型。这篇内容我梳理了2024年面试里最常碰到的CSS考点,从盒模型、Flex、Grid,到选择器优先级、定位与层叠上下文,再到底部安全区适配、样式穿透、原子化CSS这类工程化话题,把高频考点和容易踩的坑串起来。内容定位是“基础篇”,适合正在准备前端面试的人,也适合写了好几年业务代码但想回头夯实基础的朋友。

1. 盒模型:CSS布局的第一道坎

盒模型是CSS里最基础也最容易忽略的知识点。面试的时候我一般会先问候选人:“一个div的宽度设成200px,加了padding和border之后,它在页面上实际占多宽?”别小看这个问题,能准确答上来的人真不多。

1.1 标准盒模型与怪异盒模型的区别

标准盒模型(W3C盒模型)里,width指的是内容区(content)的宽度,padding和border都在这个宽度之外额外增加。怪异盒模型(IE盒模型)里,width指的是内容区 + padding + border的总宽度。这是两者最核心的区别。

用代码举个例子:

.box { width: 200px; padding: 20px; border: 1px solid #000; }

标准盒模型下,这个div实际占用的宽度是200 + 20*2 + 1*2 = 242px。怪异盒模型下,实际占用就是200px,内容区被压缩到了200 - 42 = 158px

这里有一个实操中的痛点:用标准盒模型做响应式布局时,改padding会撑破父容器。比如父容器宽度是50%,子元素设了width: 50%再加padding,就很容易溢出。这也是为什么我几乎在所有项目里都会先加这样一条全局样式:

*, *::before, *::after { box-sizing: border-box; }

border-box就是怪异盒模型。全局开启之后,写布局的时候不用反复计算宽度,padding和border都从width里面“扣”,心智负担小很多。

1.2 box-sizing的选择与实战经验

面试时还有一道变种题:“box-sizing: border-boxcontent-box分别在什么场景下用?”很多人只会背结论,说不出选择依据。

我自己的经验是:

  • 常规业务页面、组件库开发,默认用border-box,原因上面说了,省心。
  • 某些需要精确计算内容区宽度的场景,比如富文本编辑器里的图片自适应、Canvas画布大小同步,用content-box反而更容易控制。

还有一个小细节:box-sizing可继承吗?答案是不可继承。所以要么在*上统一设置,要么在公共类里显式声明,否则子元素会回到默认的content-box

注意:项目里如果用了第三方组件库(比如Ant Design、Element Plus),有的组件内部会单独设置box-sizing,接手老项目时不要想当然地全局改,容易引起样式错乱。稳妥做法是在组件外层包一个容器,在容器上单独控制。

2. Flex布局:面试必考的现代布局方案

Flex布局现在是CSS面试的“必考题”,基本没有哪场面试会绕过它。之所以大家爱问,是因为Flex的语法不算难,但很多细节一旦追问就露馅。

2.1 Flex的两条轴与核心属性

先理清楚一个基础概念:Flex布局有两根轴,主轴(main axis)交叉轴(cross axis)flex-direction决定主轴方向,row表示水平方向,column表示垂直方向。主轴的方向变了,justify-contentalign-items的效果也会跟着变。

核心属性分成两组:

  • 容器上的属性:display: flexflex-directionjustify-contentalign-itemsflex-wrapalign-contentgap
  • 项目上的属性:flex-growflex-shrinkflex-basisalign-selforder

面试里问得最多的是:justify-content: centeralign-items: center一个是水平居中一个是垂直居中吗?”这是个经典陷阱。准确说法是:justify-content控制主轴方向上的对齐,align-items控制交叉轴方向上的对齐。默认情况下主轴是水平,所以大家会直接说“水平居中、垂直居中”。但如果flex-direction: column,主轴变成垂直,justify-content: center就变成垂直居中了,这时候align-items才是水平居中。

2.2 justify-content与align-items的常见面试追问

光知道“主轴交叉轴”还不够,面试官经常会继续追问gapalign-content这些属性。gap是2021年后全面支持的一个属性,用来设置Flex项目之间的间距,以前只能用margin实现,现在直接gap: 12px就行。

align-content这个属性很多人会忽略。它跟align-items的区别是:align-content控制的是多行项目在交叉轴上的排列方式,作用于“行”这个整体;align-items控制的是单行内项目的对齐。可以这样理解,align-content管“行和行之间的间距与分布”,align-items管“行里面每个项目怎么摆放”。如果只有一行项目,align-content不生效。

实操心得:Flex做“垂直水平居中”是最常见的需求,但要注意子项在容器里内容超长时的表现。只写display: flex; justify-content: center; align-items: center;,如果子项文字太多,可能会被压缩得很丑。稳妥做法是给子项加上min-width: 0flex-wrap: wrap,让内容有换行或收缩的空间。

2.3 flex: 1 到底是什么意思

这个知识点面试出现频率极高,但能完整讲明白的人不多。flex: 1其实是三个属性的简写:flex-grow: 1; flex-shrink: 1; flex-basis: 0%;

拆开解释一下:

  • flex-grow:剩余空间怎么分。默认0,表示不放大。设为1,表示把剩余空间按比例分给这个项目。
  • flex-shrink:空间不够时怎么缩。默认1,表示空间不够会收缩。设为0,表示即使空间不够也不收缩。
  • flex-basis:项目在分配剩余空间之前的“初始尺寸”。默认auto,表示取项目的宽高。设成0,表示不考虑项目自身的内容宽度,完全平均分配。

面试里经常出这样一道题:三个item,宽度默认是100px,分别设置flex: 1flex: 1flex: 2,容器宽度是600px,问每个item最终多宽?答案是三个item先把100px拿掉,剩余300px按1:1:2分配,分别多50、50、100,所以最终是150、150、200,加起来刚好600。

这种计算题看着简单,但面试官要的就是你能不能逻辑清晰地推导出来。

3. Grid布局:二维布局的降维打击

Grid布局这两年问得越来越多,尤其是做中后台系统、大屏可视化项目的候选人,简历里都会写Grid。面试官通常会把Grid和Flex放在一起问:两者到底什么区别?什么时候用哪个?

3.1 Grid与Flex的核心区别

一句话总结:Flex是一维布局,Grid是二维布局。

Flex关注的是“一维方向上的排列”,主轴处理好,交叉轴只是辅助对齐;Grid则是把容器划分成行和列组成的网格,你可以同时控制项目在行和列两个维度上的位置。

举个例子:要把12个卡片排成3列4行,用Flex需要写flex-wrap: wrap,然后手动计算每个卡片的宽度(比如width: calc((100% - 40px) / 3));用Grid就非常简单:

.container { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; }

1fr是Grid特有的单位,意思是“可用空间的一份”。repeat(3, 1fr)表示生成3列等宽的轨道。如果改成repeat(4, 1fr),就变成4列,不需要动任何子元素样式。这就是二维布局的优势。

3.2 实际面试里的Grid高频考点

Grid里有一个细节容易被忽略:grid-template-columns: repeat(3, 1fr)grid-template-columns: 1fr 1fr 1fr等价吗?这两者效果上确实一样的,但repeat()更适合表达“重复N次”的语义,还在配合auto-fillminmax时更好用。比如:

.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); }

这行代码的意思是:自动填充尽可能多的列,每列最小200px,最大占据剩余空间。这是做响应式卡片列表的常用手法——容器宽度变化时,卡片数量自动加减,无需写媒体查询。面试时能说出这种写法,通常是加分的。

还有一道常见的题:Grid项目怎么跨列、跨行?用grid-column: span 2grid-row: span 2。这个属性在复杂报表布局里很实用。

避坑提示:Grid的fr单位不能和固定宽度直接混用而不加思考。比如grid-template-columns: 1fr 200px,那么200px先被拿走,剩余空间才属于1fr那一列。想清楚了这一点,混合布局才不会翻车。

4. 选择器与优先级:CSS的裁判规则

前面聊的布局都建立在“样式能生效”的前提下。但CSS里经常会遇到样式覆盖不掉、不生效的情况,追根溯源就是选择器优先级问题。

4.1 选择器分类与优先级计算方式

CSS选择器可以分成几大类:id选择器、类选择器、属性选择器、伪类选择器、标签选择器、伪元素选择器、通配符选择器。优先级从高到低大致是:

  • !important(最高级,慎用)
  • 内联样式(style属性)
  • id选择器
  • 类选择器、属性选择器、伪类选择器
  • 标签选择器、伪元素选择器
  • 通配符、继承样式

面试时候的标准答法是“千分位计算”:(a, b, c),a代表id选择器数量,b代表类选择器和属性选择器数量,c代表标签选择器数量。比如#app .header .nav a的优先级就是(1, 2, 1)

实际项目里最常踩的坑是过度嵌套导致优先级失控。比如写#main .container .wrap .content .title,后来想在别的地方用一个title类覆盖,就得写一长串选择器才能压住。这也是为什么很多团队坚持“类选择器为主,id选择器只用来挂载根节点”。

4.2 样式覆盖的常见误区和排查思路

样式不生效的排查思路,我一般按照下面这个顺序:

  1. 选择器优先级够不够?
  2. 是不是被!important压住了?
  3. 样式被内联style覆盖了?
  4. 样式写在的媒体查询条件没满足?
  5. 元素本身有没有display/visibility影响渲染?

面试题里喜欢问“那我加!important行不行?”行,但不推荐。!important是九阴真经,练了能打人,但反噬也大。它会让修改成本急剧上升,一旦被塞进线上代码,后面的人只能用更高的!important去覆盖,最后代码变成一团乱麻。我处理线上样式冲突的做法是:先看有没有class写错,再看优先级,最后才考虑!important

实操心得:如果你在调试工具里改样式不生效,优先怀疑优先级而不是属性写错。用Chrome的Computed面板看一下实际生效的规则来源,比凭空猜测高效得多。

4.3 BEM命名规范与样式隔离

除了优先级计算,面试里也会问到工程化层面的样式管理:你怎么保证样式不互相污染?常见的方案有BEM、CSS Modules、CSS-in-JS。

BEM是Class命名规范,全称是Block(块)、Element(元素)、Modifier(修饰符)。比如一个按钮组件,命名为.btn,它里面的图标叫.btn__icon,主色调叫.btn--primary。核心思想是:用一个扁平、语义化的类名表达结构,避免深层嵌套。

为什么BEM能减少样式冲突?因为它要求类名全局唯一,同类名的概率低。缺点也很明显:类名长,写起来啰嗦。现在的工程化方案里,不少人直接用CSS Modules或者Vue的scoped,效果也差不多。面试里能把BEM的优劣说清楚,比单纯背名字印象分高。

5. 定位与层叠上下文:堆叠世界的规则

CSS里除了“水平方向”和“垂直方向”,还有一个“Z轴方向”。很多诡异的问题,比如弹窗被遮挡、下拉菜单跑到背景后面,都是定位和层叠上下文没搞明白。

5.1 position的5个取值

position有5个常用取值:staticrelativeabsolutefixedsticky

这里面理解难点在absolute:它的定位参照物是最近的非static定位祖先。如果所有祖先都是static,那它就相对初始包含块(通常是html或body)定位。这个“最近的非static祖先”经常被忽略,面试时让候选人举例说明,很多人会卡壳。

fixed也有一个类似的“坑”:它会相对浏览器视口定位,除非某个祖先元素设置了transformperspectivefilter属性,fixed的包含块会被改变成那个祖先。这就是为什么有些弹窗写了position: fixed,结果滚动页面时弹窗跟着跑——因为它的父级用了transform

sticky是兼容性已经比较好的定位方式,它可以做到“滚动到某个位置时吸顶”,但它的生效条件是父容器必须有足够的滚动空间,且top/bottom等值要设置。如果父容器高度不够,sticky就不粘。

5.2 z-index失效的坑

z-index是面试里容易出“送命题”的知识点。很多人知道“z-index越大越在上面”,但忽略了:z-index只对定位元素(position非static)生效。一个position: static的元素,设置z-index: 9999也是无效的。

另一个容易被忽略的是层叠上下文。每当一个元素满足某些条件(比如opacity小于1、transform不为none、filter不为none、positionz-index等),它就会创建一个新的层叠上下文。层叠上下文内部的z-index无法越过父级去和外部元素比较。

举个例子:父元素A的z-index是1,子元素B的z-index是100;另一个兄弟元素C的z-index是2。B虽然z-index为100,但它不能跑到C上面,因为A(z-index: 1)整体低于C(z-index: 2)。这个机制和“公司部门与个人的关系”类似:就算你在部门里职位再高,部门整体在外部的优先级别不够,你也没法代表部门去跟别的部门比。

6. 高频CSS技巧:面试官最爱的细节题

面试进行到中段,常常会问一些“看起来简单但实际很讲究”的细节题,比如文字溢出、字体渐变、动画闪烁。这种题考察的是候选人有没有真正处理过业务里的样式难题,而不是光会写布局。

6.1 单行省略与多行省略

让超长文字显示成省略号,是前端日常开发里非常常见的需求。单行省略三件套,背下来就能用:

.ellipsis { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }

多行省略在WebKit内核下可以这样写:

.multiline-ellipsis { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }

-webkit-line-clamp指定最多显示行数,超过的部分截断并显示省略号。这个写法的兼容性在80%以上的场景都OK,但在某些内部浏览器上可能失效。如果真的要把多行省略做得很稳,可以用max-height配合line-height计算行数,写起来笨一点,但兼容性更好。

6.2 文字竖排、字体渐变与CSS mask

文字竖着排列,在传统中文排版、海报设计里很常用。CSS写法是writing-mode: vertical-rl,表示文字从上到下、从右到左排列。这个属性兼容性已经很好了,不需要额外处理。

字体渐变,面试题里的经典问法:“文字能不能用渐变颜色?”可以,用background-clip配合text实现:

.gradient-text { background: linear-gradient(90deg, #ff6a00, #ee0979); -webkit-background-clip: text; background-clip: text; color: transparent; }

这里关键的两步:background-clip: text让背景只作用于文字形状内部,color: transparent把文字本身颜色变成透明,露出背景渐变。原理不复杂,但很多不常写的人容易忘记color: transparent

mask属性也经常在面试里出现,它类似PS里的蒙版,用来控制元素哪些区域可见、哪些透明。最简单的用法是用一张PNG图的透明度作为遮罩:

.mask { mask-image: url(shape.png); mask-size: cover; }

常见的CSS动效网站喜欢用mask做图片渐隐渐显、镂空文字效果。它的兼容性在Chrome和Safari里需要加-webkit-前缀,写的时候注意。

6.3 涟漪动画、hover延迟关闭与伪元素变量控制

热词里有“css涟漪光圈扩散”,这是一种常见的交互反馈动画,点击按钮时扩散一圈圆环。实现的核心是::after伪元素加动画:

.btn { position: relative; overflow: hidden; } .btn::after { content: ""; position: absolute; width: 20px; height: 20px; border-radius: 50%; background: rgba(255, 255, 255, 0.5); transform: scale(0); animation: ripple 0.6s ease-out; } @keyframes ripple { to { transform: scale(10); opacity: 0; } }

“css hover延迟关闭”对应的是鼠标移出后,下拉菜单延迟消失的需求。用transitionvisibility配合可以实现,但更简单的方案是在伪类上设置延迟:

.menu { visibility: hidden; opacity: 0; transition: opacity 0.3s ease, visibility 0s linear 0.3s; } .parent:hover .menu { visibility: visible; opacity: 1; transition-delay: 0s; }

注意这里visibility的transition加了0.3s延迟,为的是让它先保持“隐藏”状态直到透明度动画结束,避免标签还可见时突然消失。

“css 控制伪元素变量”是指通过CSS自定义属性(CSS变量)来动态控制伪元素的内容。例如:

.tip::after { content: var(--tip-content, "默认提示"); }

然后通过JavaScript修改--tip-content,或者在不同场景下重新定义这个变量,就能做到不改CSS文件只改变量的情况下更新伪元素内容。这个技巧在做换肤、动态提示语时很实用。

避坑提示:伪元素(::before/::after)必须声明content属性,哪怕是空字符串。另外,伪元素默认是display: inline,直接设置宽高不生效,需要先display: blockposition: absolute

7. 移动端适配与兼容性:小程序和H5的痛

移动端兼容性是面试里很容易暴露项目经验深浅的部分,因为只有真实上线过、被用户设备折磨过的人,才写得出那些处理细节。

7.1 小程序苹果底部兼容方案

热词里有“小程序苹果底部兼容css”,实际上是在说iPhone的底部Home条适配问题。iPhone X及以后机型底部有一条横线,如果页面的底部操作栏盖住了它,就会导致按钮点不到、滑动不顺畅。

常规适配是用系统安全区变量:

.footer { padding-bottom: constant(safe-area-inset-bottom); /* 旧版iOS */ padding-bottom: env(safe-area-inset-bottom); /* 新版iOS */ }

在小程序里,为了兼容不同版本的iOS,很多人习惯两条都写。顺序上有讲究:constant在前,env在后,后者覆盖前者,因为新系统认识env就不认识constant了。

如果底部按钮是position: fixed定位,建议把安全区的padding加到按钮本体的box-sizing: border-box范围内,避免因为加了padding导致按钮整体高度变化、视觉上不对齐。

7.2 响应式布局与媒体查询

响应式布局的核心是媒体查询,但“用哪个断点”一直是争论点。我的经验是:不确定断点的情况下,优先用Bootstrap的断点体系——576px、768px、992px、1200px,团队协作时能减少分歧。

断点设定后,配合min-widthmax-width的选择取决于设计思路。如果项目是“移动优先”,用min-width向上兼容,代表小屏是默认样式,大屏逐级增强;如果项目是“桌面优先”,用max-width向下兼容。

这里还有一个实用技巧:与其每个断点都去调padding、margin,不如把布局维度抽出来。比如一个大卡片在大屏上是两栏,小屏上是一栏,只需要改Grid的grid-template-columns,别的样式通过Grid天然响应式自动调整。

7.3 CSS deep方法:样式穿透的原理

热词“css deep方法”经常出现在Vue组件开发里。Vue的scoped样式会在元素上添加一个>:deep(.child-class) { color: red; }

编译后的效果是把.child-class选择器的>

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询