不少朋友准备前端面试时,面对五花八门的CSS问题,心里总会犯嘀咕:感觉啥都可能问,但又不知道重点该扑在哪儿。其实吧,从这些年摸爬滚打的经验来看,虽然HTML、CSS、JavaScript常常打包出现,但CSS这块儿,总有那么十来个核心问题,反反复复出现,像钉子户一样牢牢焊在面试官的题库里。这篇文章就把这些高频硬骨头扒拉出来,配上能直接上手操作的流程,争取把这块儿整得明明白白,面试时心里不慌。
响应式网站到底咋搭?
聊到响应式,这事儿说白了就是让网页在不同设备上,都能有个得体的样子。手机上看不挤,大屏上看不空,排版和图片能自动适应。
基础三板斧
开干之前,得先备好几样基础工具。相对单位(比如%、em、rem)是根基,能让尺寸随父级或根元素变化。媒体查询则是断点设置的关键,告诉页面在什么宽度下换什么造型。流体布局确保内容容器能伸缩自如。通常大伙儿习惯移动端优先,先写好手机上的样式,再通过媒体查询往大屏扩展,这样逻辑更顺,性能也更佳。
/* 先搞定全局容器,给个大屏上限,居中显示 */
.container {
max-width: 1200px;
margin: 0 auto;
padding: 16px;
}
/* 图片得乖一点,别撑破自家门 */
img {
max-width: 100%;
height: auto;
display: block;
}
/* 等手机小屏登场,再把间距调一调 */
@media (max-width: 600px) {
.container {
padding: 8px;
}
/* 导航栏也换换姿势,比如竖着排 */
nav ul {
flex-direction: column;
}
}动手搭的时候,导航菜单在小屏上怎么处理(比如折叠起来)、图片怎么用srcset按需加载,都是加分项。弄完后,记得打开浏览器开发者工具里的设备模拟,翻来覆去地拖拽窗口大小,看看各个断点下的表现是不是都服帖。
预处理器为啥是项目常客?
以前写大项目CSS,最怕代码满天飞,改个全局颜色得翻山越岭。预处理器(像Sass、Less)就是为了解决这事儿来的。它给CSS加了变量、嵌套、混合宏这些好使的玩意儿,让代码复用和修改都轻松不少。
一个Sass实战小片段
假设要统一管理主题色,并且做一个通用的按钮样式,流程可以这样走:先定义好颜色变量,用@mixin写一个通用的阴影效果,再用@function算出间距值。之后用%btn-base定义基础按钮样式,最后通过@extend继承,配合变量和混合宏,生成最终的按钮样式。这样以后想改主题色,只需要修改变量值,所有按钮颜色会一次性更新。
// 定义主题色变量
$primary: #007bff;
$secondary: #6c757d;
$font-size-lg: 1.25rem;
// 混合宏,用来复用阴影效果
@mixin shadow($opacity: 0.12) {
box-shadow: 0 2px 8px 0 rgba(24, 39, 75, $opacity);
}
// 函数,用来计算间距值
@function space($multiplier: 1) {
@return $multiplier * 8px;
}
// 占位符,定义基础按钮样式
%btn-base {
display: inline-block;
font-size: $font-size-lg;
border-radius: 6px;
text-align: center;
cursor: pointer;
}
// 实际使用的按钮样式
.button {
@extend %btn-base;
background: $primary;
color: #fff;
padding: space(1.5) space(3);
@include shadow(0.15);
&:hover {
background: lighten($primary, 10%);
}
&.secondary {
background: $secondary;
color: #23272f;
border: 2px solid $secondary;
}
@media (max-width: 600px) {
padding: space(1) space(2);
font-size: 1rem;
}
}虽然现在原生CSS也支持变量和嵌套了,但在实际工程里,预处理器的成熟生态和强大功能,比如代码文件拆分、自动添加浏览器前缀,还是让它成为很多团队的标配。
文字咋跟着屏幕灵活缩放?
字体大小这事儿,搞不好就是“车祸现场”:手机上看字小得费眼,大屏上看字又大得愣头愣脑。关键在于不用绝对单位px,转而拥抱相对单位,让文字能根据屏幕尺寸和用户设置自适应。
几种流体字体的做法
最稳当的做法是设置根元素的font-size,然后用rem给各级标题和正文。更现代的做法是用clamp()函数,一下子搞定最小、最大和中间值,让文字平滑缩放。还可以配合媒体查询,在特定宽度下“咔嚓”一下切到另一档大小。
/* 根元素字体大小,方便后续用rem调整 */
html {
font-size: 100%; /* 默认16px,也尊重用户浏览器设置 */
}
body {
font-size: 1rem; /* 随根元素变化 */
}
h1 {
font-size: 2.5rem; /* 大标题用rem */
line-height: 1.2;
}
/* 用clamp()实现流体字,既有下限又有上限 */
h2 {
font-size: clamp(1.5rem, 4vw, 3rem);
}
/* 直接用视口宽度单位,不过得小心极端情况 */
h3 {
font-size: 6vw;
}
/* 配合媒体查询,在屏幕变大时微调正文大小 */
p {
font-size: 1rem;
}
@media (min-width: 600px) {
p {
font-size: 1.2rem;
}
}写的时候得留个心:em单位是相对父元素的,嵌套多了容易乱套,rem相对根元素更可控。另外,不能因为想追求效果就忽略了那些需要放大字体的特殊用户,绝对单位px会阻止浏览器缩放文字,尽量避免用在正文上。
z-index 失灵了?得查查堆叠上下文
有时候明明给元素设了很高的z-index,它就是死活不上来,躲在别的元素屁股后头。这通常是因为堆叠上下文在作祟。z-index只对设置了position属性(relative、absolute、fixed等)的元素有效,而一个新堆叠上下文的诞生,会把它的子元素圈起来,让子元素只在这个圈子里比大小。
堆叠上下文排查指南
假设页面上有个弹窗(.modal)怎么也盖不住遮罩层(.overlay)。先检查弹窗是否设置了position,如果没有,z-index就是摆设。如果有了,再看它的父级元素有没有触发新堆叠上下文,比如父级设置了opacity小于1、transform、filter,或者position加z-index。一旦父级创建了新上下文,子元素的z-index就只在这个父级内部有效,外面比它大的也压不住它。
/* 这个父元素自己创建了一个堆叠上下文 */
.parent {
position: relative;
z-index: 2;
background: #b3e6fc;
}
/* 子元素虽z-index高,但只限父元素内部排序 */
.child {
position: absolute;
z-index: 10;
background: #4f46e5;
}
/* 这个兄弟元素z-index虽小,但和.parent比 */
.sibling {
position: relative;
z-index: 1;
background: #fca311;
}在实际项目中,遇到z-index失效,最直接的办法就是打开浏览器开发者工具,在Elements面板里找到问题元素,看它的堆叠上下文(Computed样式里可以查到)。沿着DOM树往上找,直到发现那个“肇事”的父级,调整它的属性或者重构结构,让弹窗跳出这个上下文。
display 的 block、inline、inline-block 傻傻分不清?
这三个家伙决定了元素在文档流里的“排班表”。block是“霸道总裁”,自己占一整行,宽高随便调。inline是“社恐人士”,只占自己内容那么宽,排在一行里,宽高调不动。inline-block则是个“两面派”,既能和兄弟排排坐,又能有自己的宽高。
| 显示值 | 独自占行 | 可设宽高 | 常见元素 |
|---|---|---|---|
| block | 是 | 是 | div, p, h1 |
| inline | 否 | 否 | span, a, strong |
| inline-block | 否 | 是 | 自定义按钮、图标 |
选型指南
比如要做一组导航链接,想让它们横着排,但又可以分别控制内边距和背景色,那inline-block就是正解。如果用inline,背景色和大小就不好控制。如果用block,它们就会上下排列,不能保持一行。
box-sizing: border-box 凭啥成了最佳实践?
这事儿得从盒子模型说起。默认的content-box模型下,给元素设个width: 200px,再添padding: 20px,实际占位就变成了240px。这在布局时特别容易算崩,尤其是百分比宽度时,加个内边距就挤到下一行去了。border-box彻底解决了这个痛点,让width和height直接包含了内边距和边框。
全局应用流程
在项目里,通常会在根元素上直接统一设置,让所有元素都继承这个模型。这样,之后任何元素的宽高设定都变得非常直观。
/* 一个通配规则,让所有元素都用border-box */
*,
*::before,
*::after {
box-sizing: border-box;
}这么一设,比如再写.box { width: 200px; padding: 20px; border: 4px solid; },它的总宽度就是稳稳的200px,内边距和边框会自动从内容区域里“扣”出来。这让响应式布局里的百分比宽度计算瞬间清爽不少,不用再心算加加减减了。
图片响应式,不光是设个 max-width 那么简单
让图片响应式,最基础的当然是img { max-width: 100%; height: auto; },保证图片不超出容器,并且比例不变。但面对复杂的场景,比如要精准控制长宽比,或者在不同屏幕上加载不同分辨率的图片,就得来点高级操作了。
给图片上个“紧箍咒”——固定长宽比
有时需要把图片卡在一个固定的比例里,比如视频封面的16:9。老法子是用padding-bottom技巧,通过给父容器一个相对定位和百分比内边距来撑出比例,再把图片绝对定位填进去。现在的新玩法是直接用aspect-ratio属性,一句话搞定。
/* 老派但兼容性好的做法,手动制造一个16:9的容器 */
.image-container {
position: relative;
width: 100%;
padding-bottom: 56.25%; /* 9/16 = 0.5625 */
overflow: hidden;
}
.image-container img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover; /* 图片按比例裁剪填满 */
}
/* 现代CSS的优雅做法 */
.modern-image {
aspect-ratio: 16 / 9;
width: 100%;
height: auto;
display: block;
}除了CSS控制,还要记得在HTML里用srcset和sizes属性,告诉浏览器在不同宽度下该加载哪张图。性能这块儿也得盯着,特别是移动端网络,不能给手机加载一张几兆的4K大图。
CSS 性能咋优化,让页面丝滑起来?
CSS性能优化,说白了就是让浏览器能更快地解析样式,减少卡顿。这事儿可以从几个方向下手。
给样式“减肥”与“分流”
先得看看项目里有没有一堆压根没用的样式。可以用PurgeCSS这类工具,扫描HTML和JS,把没用到的类名从CSS里剔除,打包后的文件能瘦一大圈。接着,把样式按页面或组件拆分,用动态import()实现按需加载,这样用户打开首页时只加载必要的CSS,首屏渲染自然就快了。
选择器别写“天书”
浏览器解析CSS是从右往左的,像header nav ul li a.active这种,浏览器得先找所有.active,再回溯一堆祖先元素,效率很低。换成简洁的类名,比如.nav-link-active,匹配效率会高得多。
/* 复杂选择器,效率低 */
.header .navigation .menu-list .menu-item .menu-link.active {
color: red;
}
/* 简化后的写法 */
.nav-link-active {
color: red;
}还有一些属性,比如box-shadow、filter,在动画里用多了会导致重绘,能免则免。动画尽量用transform和opacity,它们能被浏览器单独放在一个图层里,跑起来更顺滑。平时多用浏览器开发者工具的Coverage面板,看看哪些样式没被用上,定期清理。
CSS-in-JS 还是传统 CSS 文件,到底咋选?
这问题在面试里特别容易引战。其实不用站队,把两边的优缺点掰扯清楚,再说说实际项目里怎么组合用,就能显得很稳。
| 方案 | 优点 | 缺点 |
|---|---|---|
| CSS-in-JS | 样式与组件绑定,避免冲突;动态样式强,能基于props轻易改变。 | 运行时消耗,增大了JS包体积;服务端渲染时可能闪一下无样式内容。 |
| 外部CSS导入 | 浏览器能并行加载,渲染快;文件易缓存,利于大型项目优化。 | 全局样式容易打架;基于状态动态改样式麻烦些。 |
实战混搭风
在实际的项目里,可以搞个混合策略。全局的、通用的,比如重置样式、主题变量、布局框架,统统放在传统的CSS文件里,利用浏览器缓存机制。而具体到某个组件,需要精细控制、或者样式与业务状态强相关(比如按钮加载中的状态、选中的样式),就用CSS-in-JS。这么做既能享受CSS-in-JS的封装便利,又不会丢掉全局样式的高性能和可维护性。
碰上“圣杯布局”这类复杂排版,怎么下手?
面试官甩过来一个布局需求,比如经典的“圣杯布局”(页眉、页脚固定,中间三栏,左右两栏宽固定,中间自适应),别慌。关键在于展示解决问题的思路,而不是追求唯一解。
用 Grid 搭建三栏布局的丝滑流程
思路先摆正:用CSS Grid是最简单清晰的。第一步,在父容器上定义网格,分成三列:左边栏固定宽度、中间栏自适应、右边栏固定宽度。第二步,用grid-template-areas给每个区域起个好记的名字。第三步,把页眉、中间区域、页脚放到对应的网格里。整个过程不需要任何浮动或清除浮动。
<div class="page">
<header class="header">页眉</header>
<nav class="left-nav">左侧导航</nav>
<main class="main-content">主要内容区,随着屏幕宽度缩放</main>
<aside class="right-sidebar">右侧栏</aside>
<footer class="footer">页脚</footer>
</div>.page {
display: grid;
grid-template-columns: 200px 1fr 200px; /* 左固定、中自适应、右固定 */
grid-template-rows: auto 1fr auto; /* 页眉、内容区、页脚,内容区占满剩余高度 */
min-height: 100vh;
gap: 1rem;
}
.header {
grid-column: 1 / -1; /* 页眉跨三列 */
}
.left-nav {
grid-column: 1 / 2;
}
.main-content {
grid-column: 2 / 3;
}
.right-sidebar {
grid-column: 3 / 4;
}
.footer {
grid-column: 1 / -1; /* 页脚跨三列 */
}边写边跟面试官唠,说清楚Grid如何通过grid-template-columns中的1fr单位实现中间栏的自适应,auto和1fr怎么配合让中间内容区撑开页脚。这样做不仅展示了技术能力,也体现了清晰的逻辑。
