
我最近整理了一份前端面试的 HTML CSS 八股文合集起因是前阵子帮团队做技术面试发现不少候选人简历写得很漂亮基础题却答得支离破碎。CSS 选择器优先级说不清楚、Flex 和 Grid 到底怎么选、标准文档流和浮动的影响范围……这些都是日常写页面天天碰的东西但真到了要系统讲明白的时候很多人反而卡壳。所以这篇文章不打算罗列冷门知识点而是把面试中最常问、工作中最容易踩坑的 HTML 和 CSS 核心问题按照我实际面试的考察思路重新梳理了一遍。无论你是准备跳槽的前端新人还是带项目的技术负责人这套整理都可以直接拿来当复习提纲用。1. HTML 核心考点语义化、渲染机制与元信息1.1 语义化标签为什么重要按钮和链接的使用边界面试里聊到语义化我一般先让候选人说说 HTML5 新增了哪些语义化标签。但真正拉差距的往往在“使用边界”上。比如div和span是无语义的容器前者用于块级布局分组后者用于行内内容分组。而header、nav、main、article、section、aside、footer这些是有明确语义的结构标签。难点在于标签的选择标准section表示一个独立的内容分区通常带标题article则强调内容本身可以独立分发或复用。一个简单的判断标准是如果把这段内容从页面抽出去放到别的平台它还能独立成立那就该用article反之用section就够了。按钮和链接的边界是另一个高频失分点。我会问候选人页面上有一个“了解更多”的文字点击后跳到详情页应该用a还是button很多人下意识回答用a理由是有跳转。但如果是点击后展开更多内容并不发生路由跳转呢那就该用button。判断标准其实很简单有 href 跳转语义的用a触发表单提交或页面内交互逻辑的用button。这里还要提醒一个细节a标签如果不写 href它就不是一个可聚焦的链接在无障碍和键盘操作上会出问题。前端做可访问性不是加分项在不少公司已经是硬性要求了。语义化更深层的价值是 SEO 和屏幕阅读器。比如nav能帮助搜索引擎快速识别页面主要导航区域main在同一个页面中只会出现一次它标识的是文档主体内容的起始点。很多老项目重构时候喜欢把所有div classheader直接替换成header我建议别这么干先搞清楚每个容器承担的角色再决定用哪个标签。因为header和footer在 HTML5 规范里是允许在页面多个区块内重复出现的而main只能有一个。语义化是给页面内容定义“身份”不是换一套 class 命名。1.2 标准文档流、渲染机制与 Doctype 的作用标准文档流的理解直接影响后续对盒模型和浮动布局的认知。面试我常问标准文档流中块级元素默认占满整行行内元素在同一行内从左到右排列行内元素能否设置宽度和高度答案是不能直接设置实际渲染时仍由内容撑开。这个点看起来基础但能讲清楚为什么行内元素能设置 padding 却不能设置宽高的人并不多。原因是行内元素的尺寸是由行框和内容决定的设置 width/height 不会影响布局但设置水平方向的 padding 和 margin 会影响视觉间距垂直方向的 padding 虽然视觉上会撑开背景却不会影响周围布局这导致经常出现元素重叠覆盖。这些不是死记硬背的结论而是由标准文档流的排版规则推导出来的。Doctype 的考察频率也很高。很多人只知道!DOCTYPE html是 HTML5 的声明但说不清它到底是干什么的。它的核心作用是触发浏览器的标准模式而不是怪异模式。在怪异模式下浏览器会模拟旧版 IE 的盒模型解析规则导致box-sizing计算出现偏差。实际开发中只要忘写 Doctype页面在不同浏览器下就可能出现几像素的差异最经典的就是 IE 下盒子宽度被撑大。判断是否处于怪异模式可以用document.compatMode检查返回CSS1Compat是标准模式返回BackCompat则是怪异模式。这个 API 在查兼容性 bug 时非常实用。1.3 head 区域的关键配置charset、viewport 与 SEO 元信息面试中让你写一个最简页面骨架head里至少要有三个配置。meta charsetutf-8声明页面字符编码解决中文乱码问题。网上经常传的热搜词里能反复看到!doctype html和html langzh-cn这说明最基础的骨架也是高频关注点。langzh-cn是搜索引擎判断页面语言和屏幕阅读器发音的重要依据不能随便省。meta nameviewport contentwidthdevice-width, initial-scale1.0是移动端适配的关键没有这行配置手机浏览器会自动按 980px 布局然后缩放导致字小得根本没法看。如果项目要适配 iPhone 的刘海屏等安全区域还需要设置viewport-fitcover。这里的难点不是背标签而是理解浏览器的工作机制。移动端页面的宽度不是物理像素宽度而是 CSS 像素宽度。widthdevice-width就是让布局视口的宽度等于设备宽度从根本上避免页面的“无响应式假死”。不少新手把响应式布局理解成“加了 media query 就算响应式”但如果在 viewport 上栽了跟头再多的断点也没用移动端打开依然是一坨缩得很小的桌面页。SEO 方面meta namedescription和meta namekeywords是传统写法前者在搜索结果中展示摘要后者因为被滥用已经不太被搜索引擎重视。现在更推荐加入 Open Graph 协议比如meta propertyog:title和meta propertyog:description这样页面分享到社交平台时能生成精致的卡片。前端做这些不是为了蹭 SEO 玄学而是实打实地影响产品在渠道侧的曝光点击率。2. CSS 选择器与优先级把规则彻底搞清楚2.1 选择器分类与常见面试陷阱CSS 选择器是前端面试必考的基础但把基础考出深度并不难。比如基础选择器包括标签选择器、类选择器、ID 选择器、通配符选择器。属性选择器通过[typetext]这种写法选择指定属性的元素也可以用^、$、*匹配前缀、后缀和包含关系。伪类选择器里:hover、:focus、:nth-child都是高频使用而伪元素选择器::before、::after经常被用来扩展 UI 细节。面试时我一般会让候选人说出:nth-child(2)和:nth-of-type(2)的区别能答清楚的人说明对选择器匹配机制有真实理解。前者是在所有子元素中选第二个后者是在同类标签的子元素中选第二个。还有:not()伪类的括号里能放什么、不能放什么也经常被拷问。在 CSS Selectors Level 4 标准里:not()中允许放复杂选择器但旧版浏览器对伪元素仍然有限制实际开发中谨慎嵌套使用。另一个高频易混淆点是::before和::after的书写规范单冒号是旧写法双冒号是标准写法但为了兼容 IE8过去不少团队统一用单冒号。现在浏览器环境好了新项目建议强制双冒号和伪类区分。选择器写的越复杂浏览器匹配的性能开销就越大。之前优化过一个老项目页面渲染卡顿检查后发现有一段深嵌套的后代选择器.content .box .info .title。这种写法的问题不光是它依赖的 DOM 结构必须完全一致还在于浏览器匹配是从右向左进行的每嵌套一层匹配范围都会扩大。后来的优化方式是给目标元素单独加一个 class用.info-title替代嵌套匹配。CSS 选择器的本质是“精确制导”不是“越详细越好”。2.2 优先级计算规则别再只会背 100、10、1优先级是 CSS 面试的重灾区。大多数人都知道 ID 选择器权值高于类选择器类选择器高于标签选择器但问到“内联样式、!important、继承属性”这三者同时出现时谁生效就有人开始含糊了。实际计算规则是!important具有最高优先级它提升的是一条声明不是整个选择器。然后是内联样式权重记为 1,0,0,0。接下来才轮到选择器ID 选择器是 0,1,0,0类选择器和伪类以及属性选择器是 0,0,1,0标签选择器和伪元素是 0,0,0,1。注意通配符*的优先级是 0,0,0,0而继承的样式权重是最低的任何直接命中元素的声明都会覆盖继承值。这里有个实战中特别容易踩的坑给列表项写了一个基础样式然后又用子元素选择器.list-item span去覆盖结果死活不生效。原因就是.list-item span这个选择器整体权重是 0,0,1,1而基础样式如果写在.list-item span权重是 0,0,1,1看起来相同但前者依赖 DOM 层级。真正稳妥的写法是给目标元素增加独立的 class而不是用更长的选择器去压。优先级不是拿来炫技的它是排查“样式为什么没生效”的第一把钥匙。被!important污染的代码我见过不少。新手解决样式覆盖问题时最喜欢加它短时间内看着有效但项目积累几个月后到处是!important再想调整一个字体颜色都难因为你不知道哪个!important会横插一脚。优先级冲突更合理的解法有两条一是提高选择器权重二是调整 CSS 文件中规则的顺序。相同权重下写在后面的规则会覆盖前面同名的规则这比!important好维护得多。2.3 继承机制与浏览器默认样式CSS 的继承机制很多新人理解不到位。比如给一个容器设置font-size子元素文字都会跟着变大因为 font 系列、color、line-height、text-align 这几个属性默认是可继承的。但margin、padding、border、width、height这些盒模型相关的属性默认不继承。面试时我通常用一个问题验证候选人是否真正理解继承把color: red设置在body上所有没有单独设置颜色的文字都会变红为什么因为color是可继承属性。但如果给body设置了margin: 20px子元素和它不会继承只有 body 本身会产生外边距。浏览器默认样式也是一个绕不开的话题。不同浏览器给标签预设的 margin、padding、font-size 并不完全一致这也是每个项目都要引入 reset 或 normalize 的原因。reset 的思路是把所有标签默认内外边距清零比如经典的* { margin: 0; padding: 0; }但这种方式粗暴且性能略差还会杀了ol、ul的列表符号。normalize 的思路是保留有价值的默认样式只统一各浏览器之间的差异。新项目我一般推荐用现代的 CSS reset 写法例如*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }这段代码不是把所有的默认样式全部清零而是先统一盒模型的算法让宽度计算符合直觉。box-sizing: border-box意味着一个元素设置的 width 就是最终占用的宽度padding 和 border 被包含在内不会向外撑开。这个属性是 2010 年前后前端布局里最值得记住的救命配置没有之一。3. 盒模型和布局方案穿透 CSS 布局的核心原理3.1 标准盒模型与怪异盒模型的差异以及 box-sizing 的实战意义面试中关于盒模型的经典问题一个 div 设置了width: 200px; padding: 20px; border: 5px solid #000;在标准盒模型下它的实际占用宽度是多少答案是 250px因为标准盒模型的 width 只包含 content 的宽度padding 和 border 要额外累加。而如果把box-sizing设置为border-box那么 content 的宽度会被压缩为 150px整体占用宽度保持 200px。大部分现代框架和设计系统已经把border-box作为默认值因为它的心智模型更直观切图还原时几乎不需要计算。我工作中习惯在任何项目的第一行 CSS 就写好box-sizing: border-box然后通过通配符继承给所有元素。但有一个细节需要注意如果组件库内部有自己基于 content-box 的计算逻辑通配符设置会在个别场景下出现细微的尺寸偏移。所以现在很多项目采用如下写法html { box-sizing: border-box; } *, *::before, *::after { box-sizing: inherit; }这个写法的好处是将来如果某个第三方组件需要恢复到 content-box只要单独设置那一个元素即可不会影响全局。这种“可覆盖性”在真实项目里超级重要因为团队协作时你永远预料不到别人会引入什么组件库。3.2 浮动布局的痛点与清除浮动的原理浮动这套老布局方案今天的面试价值在于理解它的存在背景和遗留问题。float最初设计是为了实现图文环绕效果后来被用来做多栏布局。浮动的核心特性是把元素从标准文档流中抽离向左或向右靠齐后续内容会环绕在它周围。这个特性带来的最大问题是父容器高度塌陷。因为浮动元素已经不在普通流中父容器的高度计算时不会把它算进去结果是父容器变成一条线背景和边框都撑不开。清除浮动的方案有很多面试时能说出两三种就够了。我推荐给不使用 flex 的老项目做维护时优先选择伪元素清浮动.clearfix::after { content: ; display: block; clear: both; }clear: both的意思是当前元素左右两侧都不允许有浮动元素。伪元素插在容器最后面强制把它放在下一个位置父容器的高度就被撑起来了。这个方法没有额外标签也不会破坏页面结构是历史方案里比较优雅的一种。新项目直接上 flex 或 grid但遇到复杂的老页面浮动仍然是无法绕开的排查对象所以对浮动的原理不能只会说“加一个 clearfix 就行了”。3.3 Flex 布局从容器属性到项目属性的完整拆解Flex 是现在面试考查的核心布局方案几乎每个候选人都会说“会用”但详细到主轴交叉轴的关系、flex: 1到底代表什么就露馅了。Flex 布局的核心模型是容器与项目的关系。容器上的display: flex会创建一个 flex 格式化上下文子元素的float、vertical-align都会失效。主轴由flex-direction决定默认row是从左到右交叉轴则是垂直于主轴。容器属性里的justify-content控制主轴对齐align-items控制交叉轴对齐flex-wrap决定是否换行gap属性用来设置项目间距。这个gap是近年新增的以前只能通过 margin 模拟不仅代码冗余还容易在首尾出现多余间距。gap在 flex 和 grid 中都可用是 CSS 布局演进中非常实用的改进。项目属性里最容易被问的是flex简写。flex: 1实际是flex-grow: 1; flex-shrink: 1; flex-basis: 0%的缩写。它的含义是项目可以放大占据剩余空间的 1 份同时允许收缩项目初始尺寸为 0。很多新人以为flex: 1是“占满一行”等到项目中两个flex: 1并排时又发现它们各占一半这才理解它的本质是“按比例瓜分剩余空间”。flex-basis单独设置的细节也不少比如flex-basis: auto会优先使用元素的 width而flex-basis: 0%则忽略 width强制从 0 开始分配。面试中我还常问align-self它允许单个项目在交叉轴上覆盖align-items设置这在实现导航栏里某个元素单独靠底时非常常用。3.4 Grid 布局与 Flex 的分工选择Grid 布局回答起来往往两极分化。用过的人能讲得条理清晰没用过的就说“听过但不熟”。Grid 是二维布局系统同时控制行和列而 Flex 更多是一维布局适合处理某个方向的排列。核心概念是网格线和网格区域。容器上的grid-template-columns和grid-template-rows定义轨道大小支持固定值、百分比、fr单位以及repeat()函数。fr是 Grid 里非常有用的弹性单位1fr 2fr表示按 1:2 分配剩余空间。区域分配靠项目上的grid-column和grid-row写法可以基于网格线例如grid-column: 1 / 3表示从第 1 条线到第 3 条线也就是跨两列。也可以使用span写法比如grid-column: span 2表示横跨 2 列这种写法更直观适合不关心具体线编号的场景。实际项目里我一般这样分工整体页面骨架和大区块布局用 Grid组件内部细节对齐和导航栏用 Flex。Grid 能轻松实现完整的页面分栏比如三行两列、侧边栏固定宽度加主体自适应Flex 则擅长把一组未知数量和大小的元素排列对齐。两套方案不是替代关系而是互补关系选型依据始终是“当前要解决的是几个维度的问题”。4. 定位机制、层叠上下文与常见布局实战4.1 position 的五个取值以及绝对定位的参照系规则position 面试题不算难但能全部讲透的人不多。static是默认值元素按标准文档流排列设置 top、left 等属性无效。relative相对自身原位置偏移元素仍然占据原本的文档流空间只是视觉上“挪走”了。absolute是完全脱离文档流参考最近的非 static 定位祖先元素进行定位如果祖先里没有定位元素就参考初始包含块通常是浏览器视口。fixed相对视口固定不随页面滚动移动常用来做吸顶导航和侧边悬浮按钮。sticky是相对定位和固定定位的结合体在滚动到某个阈值之前按普通流展示越过阈值后就“粘”在视口指定位置常用于列表页的表头吸顶。这里最大的坑是绝对定位参照系。新手往往会问“为什么我这个absolute元素没有相对它的父容器定位”原因就是父容器没有设置position: relative。所以项目里只要用绝对定位做弹窗或角标我习惯先给父容器加position: relative哪怕它本身不需要视觉位移也要让它成为定位参照系。relative作为父级参照时不会影响自身在普通流里的布局所以它的成本是最低的。sticky定位的生效也有条件不能随便设置在任意元素上。它必须在可滚动容器内部且 top、bottom 等阈值要设置到父容器尚未滚出视口的区域内。如果不生效多半是父容器高度不够或者祖先设置了overflow: hidden导致 sticky 失去滚动上下文。排查这类问题时我会从最近一级滚动容器开始检查一级一级向上找效率比盲猜高很多。4.2 层叠上下文与 z-index 失效的根因z-index 的坑在面试和实战中都很经典。z-index只作用于定位元素内static元素设置 z-index 没有效果——这是最常见的认知盲区。真正决定元素叠放顺序的规则是同一层叠上下文内比较子元素的层级不同层叠上下文之间比较祖先层级的叠放顺序子元素自身的 z-index 再大也影响不了跨上下文的比较。position: relative配合 z-index 就可以参与层级比较这也是为什么很多悬浮层要写position: relative; z-index: 10。层叠上下文的创建条件远不止定位加 z-index。opacity小于 1、transform非 none、filter非 none、will-change指定了某些属性甚至mix-blend-mode非 normal都可能导致元素创建独立的层叠上下文。实际项目中遇到过弹窗无论如何都显示在遮罩层下面的问题检查后发现弹窗的祖先上设置了transform: translateY(10px)。就是这个 transform 在父级上创建了层叠上下文导致弹窗的 z-index 再高也只在父级内部生效。这种 bug 排查起来特别费神因为代码里不会直接出现“z-index 失效”的报错线索。4.3 单列、双列、圣杯和吸底布局的实现路径布局题是前端面试的保留环节。单列布局最简单内容区域设置max-width并配合margin: 0 auto居中。双列布局以前常用float margin实现现在用 Flex 几行代码就能搞定容器display: flex侧边栏固定宽度主内容区flex: 1。需要注意侧边栏如果有卡片阴影或背景色它的高度应该由内容撑开还是和主区域等高的选择。Flex 默认会让子项拉伸成容器高度用align-items: flex-start可以撤销这个行为让侧边栏高度由内容自行决定。圣杯布局要求中间栏优先渲染、两侧宽度固定、中间自适应。Grid 的写法是我现在最顺手的一版.layout { display: grid; grid-template-columns: 200px 1fr 200px; min-height: 100vh; }控制 DOM 顺序时中间栏需要在 HTML 里放在第一位置但用 grid-column 手动指定列位置即可突破 DOM 顺序的限制。这种写法比历史方案里的双浮动加负 margin 要直观太多。吸底布局也很常见要求内容不足一屏时 footer 固定在底部内容超过一屏时 footer 自然下推。用 Grid 实现可以是根容器display: grid; grid-template-rows: 1fr auto; min-height: 100vh主内容区占用剩余空间footer 在最后一行。这套思路比过去用position: fixed底部加 padding 的方式稳健得多既不会遮挡内容也不会在内容增多时出现重叠。5. 响应式与移动端适配常见的实战问题排查5.1 媒体查询断点选择的策略以及移动优先还是桌面优先响应式设计不是什么高深魔法核心是媒体查询和弹性布局的组合。媒体查询的经典写法是media (max-width: 768px)表示视口宽度小于等于 768px 时应用内部规则。断点选择并没有标准答案不同团队会结合产品用户设备分布来定。我的习惯是优先按照内容断裂点设置断点而不是按照设备名称硬编码。比如一个卡片列表在 600px 以下排一列、600px 到 900px 排两列、900px 以上排三列那么断点就应该选 600px 和 900px。用设备类型判断很容易出问题因为 iPad、折叠屏、Surface 这些设备的尺寸差异实在太大。移动优先和桌面优先的差异在于默认样式的起点。移动优先时默认样式按手机宽度编写然后用media (min-width: 768px)逐步增强到桌面端。这种方式的优势是基础 CSS 轻量对移动端加载更友好。桌面优先则反过来默认写桌面样式再用media (max-width: 768px)适配移动端。我个人的经验是团队如果以移动端为第一用户场景移动优先更合适如果产品是复杂的后台管理系统桌面优先更自然。两者没有绝对优劣但面试时能清晰说出当前项目选择的原因比单纯背写法更得分。5.2 移动端 1px 问题和安全区域适配移动端适配的经典坑之一是 1px 边框问题。在 DPR 为 2 或 3 的屏幕上CSS 的1px实际渲染出来往往比设计稿里的物理 1px 粗。常见解法有用伪元素做缩放.border-bottom { position: relative; } .border-bottom::after { content: ; position: absolute; left: 0; bottom: 0; width: 100%; height: 1px; background: #ddd; transform: scaleY(0.5); transform-origin: 0 0; }但并不是所有场景都需要这样处理。如果设计稿里可以接受 1px 的粗线直接写border-bottom: 1px solid #ddd也没问题。高精度还原要求下再考虑伪元素缩放方案否则会引入大量额外代码。另一个细节是边框如果是在 DPR 为 3 的屏幕上scaleY(0.5)可能还不够细需要按设备动态设置缩放比例。安全区域适配是 iPhone 刘海屏和底部横条场景下的标配需求。页面有底部固定按钮、导航栏或沉浸式背景时必须要考虑env(safe-area-inset-bottom)。基本写法.footer { padding-bottom: constant(safe-area-inset-bottom); padding-bottom: env(safe-area-inset-bottom); }同时需要在meta nameviewport中加上viewport-fitcover否则安全区域的值不会被计算。这里的坑是constant()是旧版 iOS 的写法env()是新版两者要同时写并且env()要写在后面覆盖前者。小程序里苹果机型底部兼容 css 就是这个原理。实际项目里我会再配合supports判断避免非 iOS 设备上出现无谓的空白。5.3 rem 和 vw 的选择逻辑移动端适配的另一类方案是字体和尺寸单位采用rem或vw/vh。rem 需要配合根节点的font-size来计算常见的做法是用 JS 根据视口宽度动态调整html的 font-size或者使用vw直接以视口宽度的百分之一为单位。vw 方案的好处是不依赖 JS天然响应式缺点是做大屏适配时如果按宽度缩放高度较大时会出现内容过大的问题。rem 方案更可控但引入了一段动态计算脚本有点“侵入性”。近几年我的新项目更倾向于直接采用clamp()配合 rem 或 px 来限定字号范围这样既能响应式又不会出现小屏字太小、大屏字太大的尴尬。面试时能讲明白clamp(16px, 2vw, 24px)的算法逻辑说明对响应式字体的理解已经不是停留在“用 rem”这个层面了。6. 高频综合题与手写实现的关键套路6.1 水平垂直居中的所有方案整理手写水平垂直居中几乎是前端面试必考题也是平时开发里最常被问的 CSS 问题。方案至少要有四五种而且候选人需要说出各自的适用场景和坑。先说最简单的一类只需要水平居中的场景文本或行内元素用text-align: center块级元素用margin: 0 auto。要水平垂直同时居中Flex 方案最优.parent { display: flex; align-items: center; justify-content: center; }这是一种零心智负担的写法兼容性好适用于绝大多数业务组件。如果父容器显示grid同样可以写.parent { display: grid; place-items: center; }place-items是align-items和justify-items的简写一条声明实现双轴居中非常克制。第三种是绝对定位加 transform.child { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); }这种方案不要求父容器是 Flex自适应宽高但需要注意transform会创建新的层叠上下文可能影响弹窗层级。第四种是绝对定位加 inset 为 0 再加 margin auto.child { position: absolute; inset: 0; margin: auto; width: 200px; height: 100px; }容器需要明确宽高但代码非常简洁适合已知尺寸的固定弹窗或浮层。最后还有 table-cell 方案父容器display: table-cell; text-align: center; vertical-align: middle子元素设置为display: inline-block。这种方案在兼容老浏览器场景下还有用新项目基本不推荐了。不同的方案背后的布局上下文完全不同面试官想听到的是“我知道这些方案底层分别依赖什么机制”。比如 Flex 方案依赖 flex 格式化上下文绝对定位方案依赖包含块和 transformtable-cell 方案依赖 table 布局的 vertical-align。理解了底层逻辑才能根据项目场景正确选择而不是背一堆代码却不知道什么时候用哪一行。6.2 CSS 动画的 transform、transition 与 animation 选型CSS 动画现在也是前端必备技能热搜词里“css动画效果网站”说明大家对手写动画有真实需求。基础要掌握transform的位移、缩放、旋转和倾斜。rotate3d是三维旋转参数rotate3d(x, y, z, angle)中的前三个数字构成旋转轴向量angle 是角度。例如transform: rotate3d(1, 0, 0, 45deg)表示绕 X 轴旋转 45 度。这个属性在做卡片翻转、3D 展示时非常常用但需要配合perspective才能看到立体效果否则只是平面上的拉伸变形。perspective设置在父容器上值越小透视效果越强烈常用值在 600px 到 1000px 之间。transition是两个状态之间的平滑过渡适合 hover 效果比如按钮颜色渐变。animation则适合定义完整的关键帧动画可以设置循环次数、延迟、动画曲线等。性能方面动画优先操作transform和opacity尽量避免对width、height、top、left这些属性做动画因为他们会触发布局重排和重绘在移动端容易卡顿。浏览器对 transform 和 opacity 的动画可以放在合成线程上处理不阻塞主线程帧率更稳。手写一个常见进场动画可以参考下面的结构keyframes fadeInUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } .item { animation: fadeInUp 0.4s ease both; }animation-fill-mode: both的含义是动画开始前应用 from 状态结束后保留 to 状态防止元素在动画播放前闪现或结束后跳回原样。这个细节经常被忽略导致动画出现明显的闪动。面试手写动画题时能主动提到 fill-mode说明你真的在项目里处理过它。6.3 原子化 CSS 与深选择器在实际团队里的应用争议现在团队里越来越多人聊原子化 CSS相关热搜词也出现了“原子性css”。它的思想是把每一个样式声明拆成一个独立的类比如.mt-4 { margin-top: 16px; }使用时直接在 HTML 上堆类名。这样的好处是样式复用性极高几乎不写重复的 CSS 文件打包体积也小。同时由于 class 和样式一一对应修改一处可以影响全局适合统一设计变量的场景。但缺点也很明显HTML 类名会变得很长且视觉结构被拆得很碎阅读体验依赖开发者的套路熟练度。css deep方法则主要出现在 Vue 单文件组件中用来穿透 scoped 样式的边界。Vue 的 scoped 会给组件元素加上 data 属性子组件内部的标签默认不受父组件 scoped 样式影响。如果一定要改子组件内部样式标准的写法是:deep()例如.parent :deep(.child-class) { color: red; }注意:deep()前面空格的问题如果写在组件根元素上可能完全不生效。这种情况下把选择器改成:global()或者给子组件根节点加一个特定的 class会更稳定。面试里能讲清楚 scoped 属性选择器的工作机制再结合:deep()的实际场景会是很扎实的加分项。6.4 前端工程化后的强制约束与规范配合面试聊到 CSS 工程化候选人通常会说“团队用 SCSS 嵌套写样式打包时用 PostCSS 处理厂商前缀”。但问到是否做过样式规范落地答案就少多了。前端开发规范里的 HTML 和 CSS 部分核心是缩进、命名和注释风格。CSS 命名方式我推荐 BEM 风格块元素修饰符虽然它写起来类名长但可阅读性和可维护性都非常好。比如一个按钮组件的结构是.btn,.btn--primary,.btn__icon所有人都能一眼看出元素之间的关系也方便写选择器定位问题。有了 BEM 约束配合 scoped 或 CSS Modules几乎不会出现样式互相污染的烦恼。规范不是嘴上定的需要工具强制。项目中可以配置 stylelint检查属性顺序、嵌套深度、颜色变量使用等配合 Prettier 统一格式化。提交前跑 lint-staged不通过的代码无法提交比贴规范文档有效得多。团队如果选了原子化 CSS 路线也要有配套的类名设计规范不然每个人写出来的样式类名风格完全不同最后维护成本反而更高。6.5 微前端与 CSS 隔离的实践心得微前端在团队协作场景中越来越普及相关热词也说明关注度很高。微前端的接入方式有 iframe 和 JS 运行时加载两种。iframe 天然隔离样式和 JS但有通信成本高、路由不同步、SEO 不友好等问题。JS 运行时加载可以让不同子应用共享页面上下文但样本隔离就成了大问题。最常见的是子应用的全局样式污染了主应用或其他子应用的界面。处理思路有几条。第一是约定子应用内的全局样式限定在自身挂载节点下比如统一加.app-root前缀但这种方式依赖人的自觉容易漏。第二是使用 Shadow DOM 天然隔离很多微前端框架支持配置 shadow 模式。不过 Shadow DOM 也有坑比如弹出层挂到 body 上时会丢失内部样式需要另想办法。第三是主应用和子应用导入时统一走 CSS Modules 或者给子应用样式加随机前缀。我在实际项目里优先采用“前缀约束 在子应用入口统一注入重置样式”的组合方式基本能把冲突控制在可接受范围内。面试聊到微前端时能主动说出 CSS 隔离方案比只背框架概念要加分不少。6.6 性能优化里与 CSS 相关的细节HTML 和 CSS 相关的性能优化点我在面试里通常让候选人从渲染路径的角度讲。主流程是 HTML 解析、CSSOM 构建、JavaScript 执行、布局、绘制和合成。CSS 文件过多、过大会阻塞渲染因此首屏 CSS 需要尽量小复杂动画的样式不要混在首屏样式里。CSS 选择器的匹配方向是从右到左类名选择器的匹配效率高于标签选择器但现代浏览器对大多数场景已经优化得很好单纯为了性能去过度重写选择器意义不大更关键的是减少无效的重绘和重排。减少重排的有效手段包括避免频繁读取和修改布局属性使用requestAnimationFrame批量处理动画尽量用transform和opacitywill-change可以提前告知浏览器某些元素会变化但要慎用滥用会让浏览器一直维持合成层增加内存开销。还有一个比较隐蔽的点是import引入 CSS 会阻塞渲染因为浏览器需要等这个文件下载完成后才能继续构建 CSSOM。线上建议全部改成link标签或者直接打包合并进主样式文件。HTML 侧的性能优化则体现在标签语义化、减少无意义的嵌套层级、图片尺寸合理、loadinglazy用于非首屏图片等这些细节往往比后端优化更容易立竿见影。7. 常见报错与样式排查经验速查7.1 样式不生效的排查路径我在团队里带新人时经常收到的问题是“我这个样式为什么没生效”。排查顺序基本是固定的。先看选择器是否写错比如类名大小写、class 和 HTML 里是否完全一致。然后在浏览器开发者工具里看 Elements 面板找到目标元素检查 Computed 样式中该属性是否被其他规则覆盖。如果被覆盖就要看是哪条规则、哪个文件、权重是多少。!important会把优先级拉满所以只要看到样式没生效第一反应就是搜索代码里是否有!important覆盖了它。如果 Computed 中显示的是继承值说明没有规则直接命中这个元素可能选择器把父子关系写反了也可能元素压根没有匹配上。如果 Computed 中显示的是 initial 或 unset说明属性没有继承也没有直接命中。还有一个高频点是 CSS 文件本身没有加载成功比如路径问题或打包配置里的 publicPath 不对。此时可以在 Sources 面板里确认 CSS 是否正常请求返回Network 面板里状态码是否是 200。开发者工具的 Coverage 面板也能帮助判断是否有大量 CSS 根本没被使用这种情况在维护老项目时很常见压缩后文件很大但实际用到的样式寥寥无几。7.2 高度塌陷、外边距合并与滚动条污染高度塌陷问题最常见于浮动布局和绝对定位脱离文档流的场景。父容器只包含浮动元素时高度计算默认不包含它们解决方式就是用 clearfix 或在容器上设置overflow: hidden、display: flow-root。display: flow-root是一个比较新的方案它创建一个块级格式化上下文BFC让容器内的浮动和普通流重新参与高度计算而且没有overflow: hidden会裁剪内容的副作用。不过老浏览器兼容性不如 clearfix新项目可以放心用老项目还是保持 clearfix 稳妥。外边距合并指上下两个元素的垂直 margin 会取较大值而不是相加。比如上面元素margin-bottom: 20px下面元素margin-top: 30px最终间距是 30px 而非 50px。这个特性是标准行为但新手很容易困惑。解决方式有给其中一个元素改成 padding或把其中一个元素放进 BFC 中。实际项目里我很少直接对抗外边距合并因为只要能理解它写样式时自然能控制和规避。滚动条污染是说某些组件内部需要滚动比如弹窗内的内容过多但没有设置合适的overflow-y: auto导致整个页面跟着滚动或者在滚动容器上误触了overflow: hidden结果弹窗无法滚动。排查这类问题时要把滚动容器、高度约束和 overflow 三个属性一起看。7.3 真实项目中的 CSS 细节规范团队协作中CSS 代码的混乱程度往往远超预期。我在实际项目里推行过几条可落地的规范。第一条是属性顺序统一定位、盒模型、排版、视觉、动画。比如position和z-index放最前面然后是display、width、height、margin、padding再是font-size、color、line-height末尾放background、border、transform、transition。这套顺序是为了快速扫读而不是死板的标准。第二条是颜色和间距统一使用设计变量禁止在代码里随手写死颜色值除非是特殊状态的临时色。第三条是尽量减少嵌套选择器深度SCSS 里嵌套超过四层基本该重构了因为深层嵌套生成的选择器权重高、匹配范围模糊后续覆盖特别费劲。这些规范配合 stylelint 强制执行之后项目的样式维护成本肉眼可见地下降。8. 面试准备建议与复习路径8.1 前端面试题 2026 的趋势和考察重点从近年前端面试的反馈来看简单的“八股文”背诵越来越不够用。很多公司开始把问题嵌到具体业务场景里比如“这个需求让你实现一个拖拽排序的卡片列表CSS 部分你会怎么设计和优化”“如果弹窗在小屏手机上溢出你会如何调整布局”。这种场景化面试要求候选人不光知道属性还要能权衡方案。因此准备 HTML 和 CSS 时建议多拿真实的页面组件来做练习比如实现一个响应式导航栏、一个带交互动效的按钮、一个固定底部的对话输入框然后思考边界情况、可访问性和性能问题。搜“前端面试题2026”的时候可以先扫一眼热门考点分布但不用迷信押题基础扎实才是长期有效的能力。还有一点是现在不少面试官会主动考察浏览器渲染机制和 CSS 性能不再只问“会不会用 flex”。比如他们会问你如何减少页面重排重绘为什么transform动画更快如何处理图片加载导致的布局偏移。这里面很大一部分内容来自 HTML 和 CSS 的真实运行机制而不只是样式声明本身。刻意去理解浏览器的工作原理、熟悉 DevTools 的 Performance 面板对面试和实际项目都有实打实的帮助。8.2 日常积累的方法从“会用”到“能讲清楚”给准备面试的朋友一个具体建议每掌握一个 CSS 属性就尝试自己写一遍简短的演示页面并把“为什么这样写”用两句话讲出来。比如学会了position: sticky就试着自己实现一个表格吸顶表头学会了 Grid 的grid-template-areas就试着用它在桌面端排一个带侧边栏的页面。用输出倒逼输入比死记硬背效果好太多。平时看别人的源码时重点不是抄代码而是分析人家为什么这么布局。看到一张设计稿第一步先分层第二步定布局方案第三步处理细节。这套思维模式练多了面试时拿到题目自然能快速产生结构化的解决思路。HTML 和 CSS 看起来简单但它们的边界极广。今天整理的内容覆盖了语义化标签、文档流、盒模型、选择器优先级、Flex、Grid、定位、层叠上下文、响应式适配、动画、原子化 CSS、微前端样式隔离等核心考点也穿插了我在真实项目中踩过的坑和排查路径。对于刚开始准备前端面试的朋友建议把高频的布局题和手写方案优先练熟对于有经验的开发者可以重点复盘自己项目里是否真的把这些基础规则应用到位了。前端这个行业更新快但 HTML 和 CSS 的底层原理相对稳定把基础打牢后面学再新的框架都不慌。最后再分享一个小技巧把所有面试里可能被问到的 CSS 问题整理成一份清单每周末挑一个题目用 20 分钟手写实现再花 10 分钟对着代码向自己讲解一遍。坚持一两个月后你会明显感觉到对页面布局和样式规则的掌控力上升了一个量级。这个习惯不只是为了应付面试它本身就是提升前端基本功的最有效路径。