CSS(面试)

1.CSS选择器优先级?

!important > 内联样式 > ID 选择器 > 类选择器 = 属性选择器 = 伪类选择器 > 标签选择器 = 伪元素选择器 > 通配选择器

行内样式(1000)>ID选择器(100)>类选择器(10)>标签选择器(1)>通用选择器(0)

各项示例:

内联样式:写在标签属性style的样式,如 <p style="color=red">

ID选择器,如#id{...}

类选择器,如 .class{...}

属性选择器,如 input[type="email"]{...}

伪类选择器,如a:hover{...}

伪元素选择器,如 p::before{...}

标签选择器,如 input{...}

通配选择器(匹配每一个元素),如 *{...}

2.引入CSS文件的几种方式

|----------|--------------------|----------------------------------|---------------------------------------|
| 特性 | 内联样式(Inline CSS) | 内部样式表(Internal CSS) | 外部样式表(External CSS) |
| 优先级 | 最高 | 中 | 最低 |
| 代码位置 | 在HTML元素的style属性中 | 在HTML文档的<head>标签内的<style>标签中 | 在单独的.css文件中,通过<link>标签或者@import引入 |
| 适用场景 | 快速修改单个元素样式 | 单页面样式,简单页面 | 多页面共享样式,复杂项目 |
| 可维护性 | 最差 | 较差 | 最好 |
| 性能 | 无缓存 | 无缓存 | 可缓存,性能最佳 |

3.<link>和@import的区别

|----------|-------------------------------------------------------------------|---------------------|
| 特性 | @import | <link> |
| 加载方式 | 阻塞式加载(遇到import就会暂停当前css文件加载而去加载引入的),加载顺序影响样式生效(顺序加载的,后面的可能会覆盖前面的) | 非阻塞式加载,支持异步加载 |
| 性能 | 较差,可能导致页面渲染延迟 | 较好,适合多文件加载 |
| 使用位置 | 在CSS文件内部 | 在HTML文件的<head>标签中 |
| 适用场景 | 小型项目,模块化CSS文件 | 大型项目,多页面共享样式 |

4.CSS哪些元素可以继承,哪些不行?

可继承:主要文本相关属性,如文字颜色与大小(color、font-size等),列表样式(ul中的li)(list-style),可见性(visibility);

不可继承:大多数 CSS 属性是不可继承的,比如宽高、定位,背景相关属性(background)

控制继承:强制继承父元素(color: inherit;),强制设置默认值(color: initial;),父元素有就继承,没有使用默认值(color: unset;)

5.CSS盒子模型?

盒子模型就是把html元素看成一个矩形盒子,分为标准盒子模型和怪异盒子模型

内容区域(Content)、内边距(Padding)、边框(Border)和外边距(Margin)

|---------------------------------|--------------------------------------------|-------------------------------|
| 特性 | 标准盒子模型(Standard Box Model) | 怪异盒子模型(Quirks Mode Box Model) |
| 尺寸计算 | widthheight仅指内容区域 | widthheight包括内容区域、内边距和边框 |
| 总宽度计算 | width + padding + border + margin | width + margin |
| 总高度计算 | height + padding + border + margin | height + margin |
| 触发方式 | 使用正确的DOCTYPE声明(如<!DOCTYPE html>) | 没有DOCTYPE声明或使用错误的DOCTYPE声明 |
| 兼容性 | 符合W3C规范,适用于现代网页开发 | 用于兼容旧的HTML和CSS代码 |
| 设置(能达到宽度尺寸的效果,但是不能说是怪异盒子模型) | box-sizing:content-box | box-sizing:border-box |

6.浮动是什么,怎么清除

浮动是使元素沿着其包含块的左侧或右侧放置,同时允许文本和内联元素环绕在其周围。

复制代码
float: left | right | none | inherit(继承父元素);

问题:父元素高度塌陷,影响后续元素布局

解决:

1.添加空div,空元素设置为clear:both

2.伪元素:

.container::after {

content: '';

display: block;

clear: both;

}

3.设置overflow为hidden或者auto,触发BFC

7.什么是BFC,如何触发

块级格式化上下文,独立的渲染区域,与外部样式不相干

规则:内部块级盒子会垂直方向上一个个排列,同一个BFC内相邻块级盒子编剧会发生重叠,计算高度的时候浮动元素高度也参与计算

作用:清除浮动,避免外边距重叠,实现自适应布局(避开浮动元素)

如何触发:

float不为none

position为absolute或fixed

display为inline-block、table-cell(表格单元格)、table-caption(表格标题)

overflow不为visible

display: flow-root:这是专门为创建 BFC 而设计的新值

8.CSS边距重叠是什么,如何解决?

某些情况下,块级元素的垂直外边距(margin-top和margin-bottom)会合并为一个边距

如:

  1. 相邻兄弟元素之间的边距重叠

当两个或多个相邻的块级元素垂直排列时,它们的上下边距可能会重叠。例如,如果第一个元素有margin-bottom: 20px;,第二个元素有margin-top: 30px;,那么它们之间的实际间距将是30px,而不是50px。

  1. 父元素与第一个或最后一个子元素之间的边距重叠

当一个元素包含在另一个元素中时,如果父元素没有边框(border)、内边距(padding)或行内内容来分隔它们,父元素的上外边距(margin-top)或下外边距(margin-bottom)会与子元素的上外边距或下外边距发生重叠

  1. 空块级元素的边距重叠

对于没有任何内容、填充(padding)、边框(border)的空块级元素,它的上下边距也会发生重叠

解决:

1.添加边框或内边距

2.使用浮动或绝对定位

3.变成行内块元素

4.创建新的BFC

5.使用伪元素来填充

9.常用的布局方案

1.普通流布局:按照html顺序依次排列,块级元素独占一行,行内元素一行内排列

2.浮动布局:元素脱离普通流,向左或向右浮动,其他内容环绕浮动元素

3.定位布局:脱离普通文档流,通过position控制元素位置

  • static:默认值,不定位
  • relative:相对自身位置偏移
  • absolute:相对最近的定位祖先元素
  • fixed:相对浏览器窗口
  • sticky:粘性定位(相对父元素和视口)

4.flex布局(弹性布局)

5.grid布局

6.表格布局(dispaly:table)

7.响应式布局

10.讲解一下flex布局?

display:flex或inline-flex(行内元素)(按钮、标签页、工具栏等)

主要属性:

flex- direction:定义主轴方向

|------------------|----------------|
| | 含义 |
| row(默认) | 主轴为水平方向,从左到右排列 |
| row-reverse | 主轴为水平方向,从右到左排列 |
| column | 主轴为垂直方向,从上到下排列 |
| column-reverse | 主轴为垂直方向,从下到上排列 |

flex-wrap:控制字元素是否换行

|----------------|-----------------|
| 值 | 含义 |
| nowrap(默认) | 不换行,子元素会被压缩 |
| wrap | 自动换行,第一行在上方 |
| wrap-reverse | 自动换行,第一行在下方(反向) |

flex-frow:flex- direction和flex-wrap的缩写

justify-content:主轴上的对齐方式

|------------------|-----------------|
| 值 | 含义 |
| flex-start(默认) | 靠主轴起点对齐 |
| flex-end | 靠主轴终点对齐 |
| center | 居中对齐 |
| space-between | 子元素之间均匀分布,首尾贴边 |
| space-around | 子元素之间均匀分布,首尾有间距 |
| space-evenly | 子元素之间和首尾间距都相等 |

align-items:交叉轴上的对齐方式

|---------------|----------------|
| 值 | 含义 |
| stretch(默认) | 子元素拉伸填满容器高度 |
| flex-start | 靠交叉轴起点对齐 |
| flex-end | 靠交叉轴终点对齐 |
| center | 居中对齐 |
| baseline | 以子元素的第一行文字基线对齐 |

align-content:多行内容在交叉轴上的对齐方式(之后在flex-wrap:wrap且子元素换行时生效)

|-----------------|----------------|
| 值 | 含义 |
| stretch(默认) | 多行拉伸填满交叉轴 |
| flex-start | 多行靠交叉轴起点对齐 |
| flex-end | 多行靠交叉轴终点对齐 |
| center | 多行居中对齐 |
| space-between | 多行之间均匀分布,首尾贴边 |
| space-around | 多行之间均匀分布,首尾有间距 |

子元素属性:

flex-grow: 定义项目的增长比例(默认为 0)。

flex-shrink: 定义项目的缩小比例(默认为 1)。

flex-basis: 定义项目在分配多余空间之前,占据的主轴空间(默认为 auto)。

flex: 是 flex-grow, flex-shrink, 和 flex-basis 的简写,方便用于快速定义。

align-self: 允许单个项目有不同的对齐方式,覆盖 align-items。

flex:1

是让元素自动填满剩余空间的神器,常用于等宽布局、自适应布局、左右结构等场景。

flex-grow: 1; flex-shrink: 1; flex-basis: 0%;

|---------------|------|------------------------|
| 属性 | 值 | 含义 |
| flex-grow | 1 | 元素可以增长,参与分配剩余空间 |
| flex-shrink | 1 | 元素可以缩小,空间不足时会收缩 |
| flex-basis | 0% | 元素初始占据的空间为 0,完全由增长决定大小 |

11.em、rem、vw、vh?

em:相对父元素的字体大小,适合细致控制间距和排版。

rem:相对根元素的字体大小,使用更为一致,便于管理全局样式。

vw:根据视口宽度调整,适合响应式布局。

vh:根据视口高度调整,适合适应屏幕的元素高度。

12.如何实现响应式布局?

  • 媒体查询:@media (min-width: 600px);

不同的媒体用不同的样式

  • 流式布局:emrem、 百分比(Bootstrap 的 12 栅格)等相对单位设定;
  • vwvh方案
  • flex 方案
  • grid 方案

13.讲解一下grid布局?

容器属性

|-------------------------|----------------|----------------------------------|
| 属性名 | 说明 | 常用值示例 |
| display | 设置为 Grid 容器 | grid / inline-grid |
| grid-template-columns | 定义列的数量和宽度 | 200px 1fr repeat(3, 100px) |
| grid-template-rows | 定义行的数量和高度 | auto 1fr 100px |
| grid-template-areas | 定义区域布局(命名区域) | "header header" "sidebar main" |
| grid-gap / gap | 设置网格间距 | 10px / row-gap column-gap |
| justify-items | 子项在单元格中水平对齐方式 | start / center / end |
| align-items | 子项在单元格中垂直对齐方式 | start / center / end |
| justify-content | 整个网格在容器中水平对齐方式 | center / space-between |
| align-content | 整个网格在容器中垂直对齐方式 | center / space-around |

子元素属性

|---------------------|------------------------------------------------------|----------------------------|
| 属性名 | 说明 | 常用值示例 |
| grid-column-start | 子项从哪一列开始 | 1 / span 2 |
| grid-column-end | 子项到哪一列结束 | 3 / span 2 |
| grid-row-start | 子项从哪一行开始 | 1 / span 1 |
| grid-row-end | 子项到哪一行结束 | 2 / span 1 |
| grid-column | 简写:start / end | 1 / 3 |
| grid-row | 简写:start / end | 1 / 2 |
| grid-area | 简写:row-start / column-start / row-end / column-end | 1 / 2 / 2 / 3 |
| justify-self | 子项在单元格中水平对齐方式 | start / center / end |
| align-self | 子项在单元格中垂直对齐方式 | start / center / end |

  • px, em, % 等绝对/相对单位。
  • fr (fraction ): 代表网格容器中可用空间 的一份。例如 1fr 2fr 表示两列,比例为 1:2。
  • auto: 根据内容自动调整大小。
  • minmax(min, max): 定义轨道大小的范围。例如 minmax(100px, 1fr) 表示最小 100px,最大不超过 1fr。
  • repeat(count, track-list): 重复定义轨道

14.CSS3新特性

|-----------|-------------------------------|-----------------|--------------------------------------------------------------|
| 分类 | 特性 | 说明 | 示例 |
| 选择器 | :nth-child() | 选择第 n 个子元素 | li:nth-child(2) |
| | :nth-of-type() | 选择第 n 个指定类型的子元素 | p:nth-of-type(3) |
| | :not() | 否定选择器 | div:not(.active) |
| | ::before / ::after | 伪元素 | div::before { content: ''; } |
| 盒模型 | box-sizing | 控制盒模型计算方式 | box-sizing: border-box; |
| 背景与边框 | border-radius | 圆角边框 | border-radius: 10px; |
| | box-shadow | 盒子阴影 | box-shadow: 0 2px 5px rgba(0,0,0,0.3); |
| | background-size | 背景图大小 | background-size: cover; |
| | background-clip | 背景绘制区域 | background-clip: padding-box; |
| | background-origin | 背景定位区域 | background-origin: content-box; |
| | multiple backgrounds | 多背景图 | background: url(a.png), url(b.png); |
| 文本效果 | text-shadow | 文字阴影 | text-shadow: 1px 1px 2px #000; |
| | word-wrap / overflow-wrap | 自动换行 | word-wrap: break-word; |
| | @font-face | 自定义字体 | @font-face { font-family: MyFont; src: url(...); } |
| 渐变 | linear-gradient() | 线性渐变 | background: linear-gradient(to right, red, blue); |
| | radial-gradient() | 径向渐变 | background: radial-gradient(circle, red, blue); |
| 动画与过渡 | transition | 过渡动画 | transition: all 0.3s ease; |
| | @keyframes + animation | 关键帧动画 | @keyframes slide { from { left: 0; } to { left: 100px; } } |
| 多列布局 | column-count | 列数 | column-count: 3; |
| | column-gap | 列间距 | column-gap: 20px; |
| | column-rule | 列分隔线 | column-rule: 1px solid #ccc; |
| 媒体查询 | @media | 响应式布局 | @media (max-width: 600px) { ... } |
| 弹性布局 | display: flex | 一维布局 | display: flex; |
| | flex-direction | 主轴方向 | flex-direction: column; |
| | justify-content | 主轴对齐 | justify-content: center; |
| | align-items | 交叉轴对齐 | align-items: center; |
| 网格布局 | display: grid | 二维布局 | display: grid; |
| | grid-template-columns | 定义列 | grid-template-columns: 1fr 1fr; |
| | grid-gap / gap | 网格间距 | gap: 10px; |
| 变换 | transform | 2D/3D 变换 | transform: translateX(100px); |
| | transform-origin | 变换中心点 | transform-origin: top left; |
| 过渡 | transition | 属性过渡 | transition: background 0.3s ease; |
| 其他 | opacity | 透明度 | opacity: 0.5; |
| | rgba() / hsla() | 颜色透明度 | color: rgba(0,0,0,0.5); |
| | calc() | 动态计算 | width: calc(100% - 20px); |
| | filter | 滤镜效果 | filter: blur(5px); |
| | will-change | 性能优化提示 | will-change: transform; |

15.@keyframes和transition?

@keyframes:关键帧动画

用于定义复杂动画,可以控制动画在多个时间点的状态

复制代码
@keyframes 动画名 {
  from {
    /* 起始状态 */
  }
  to {
    /* 结束状态 */
  }
}
示例:
// animation: 动画名 持续时间 动画方式 延迟 次数 方向;
.box {
  animation: move 2s ease-in-out infinite alternate;
}
@keyframes move {
  0%   { transform: translateX(0); }
  50%  { transform: translateX(100px); }
  100% { transform: translateX(0); }
}
transition:过渡动画

用于在属性值发生变化时(如 hover、点击等)平滑过渡

vb 复制代码
`// transition: 属性名 持续时间 动画曲线 延迟;
.btn {
  background: blue;
  color: white;
  padding: 10px 20px;
  transition: background 0.3s ease;
}
.btn:hover {
  background: green;
}
`

16.css预处理器?

|----------|----------------------------|----------------------|
| 特性 | Sass | Less |
| 编译器 | Dart Sass(官方推荐) | Less.js(官方) |
| 运行环境 | 仅支持构建时编译 | 支持浏览器端实时编译(开发) |
| 构建集成 | 与 Webpack、Vite、Rollup 集成良好 | 需配置 Less Loader,集成稍弱 |
| 性能 | Dart Sass 编译速度较快 | Less.js 稍慢,尤其在浏览器端 |

支持变量、嵌套、混合(Mixin)、函数、模块化

复制代码
变量
$primary-color: #3498db;
.button {
  background: $primary-color;
}

嵌套
.nav {
  ul {
    list-style: none;
    li {
      display: inline-block;
    }
  }
}

混合
@mixin rounded($radius) {
  border-radius: $radius;
}
.box {
  @include rounded(10px);
}
使用 @mixin 定义了一个名为 rounded 的 混合(mixin)。
它接受一个参数 $radius,表示圆角的大小。
在大括号 {} 中定义了要复用的 CSS 样式:border-radius: $radius;

|----------------|-----------------------|-------------------|
| 特性 | Sass | Less |
| 变量定义 | $color: red; | @color: red; |
| 混合(Mixins) | @mixin + @include | .mixin-name() |
| 继承 | @extend .class | :extend(.class) |
| 条件语句 | 支持 @if@for 等 | ❌ 不支持 |
| 函数 | 内置丰富函数 | 内置较少 |
| 语法风格 | SCSS 或缩进语法 | 类似 CSS |
| 运算 | 直接支持 | 需加括号 |
| 导入 | @import@use | 仅 @import |

17.重排和重绘?

  • 重排(reflow):元素的的位置和尺寸大小变化;
  • 重绘(Repaints):外观发生改变,但没有改变布局;

减少重排:

|--------------------------------------|------------------------------------|----------------------------------------|
| 方法 | 说明 | 示例 |
| 批量修改 DOM | 使用 DocumentFragment 或离线 DOM 批量操作 | fragment.appendChild(node) |
| 避免频繁读取布局属性 | 如 offsetTopclientWidth 会触发重排 | 缓存这些值 |
| 使用 absolute fixed定位 | 脱离文档流,减少对其他元素影响 | position: absolute |
| 避免使用表格布局 | 表格元素重排代价高 | 使用 div + flex |
| 使用 transform替代 top/left | transform 不会触发重排 | transform: translateX(100px) |
| 使用 requestAnimationFrame | 把 DOM 操作集中在下一帧 | requestAnimationFrame(() => { ... }) |
| 避免逐条修改样式 | 合并样式修改,减少重排次数 | element.style.cssText = '...' |

减少重绘:

|--------------------------------------------------|-------------------|-----------------------------------|
| 方法 | 说明 | 示例 |
| 使用 class替代 style修改 | 通过切换 class 批量修改样式 | element.classList.add('active') |
| 使用 transform opacity做动画 | 只触发合成层更新,不触发重绘 | transform: scale(1.1) |
| 避免频繁修改颜色、背景等视觉属性 | 这些属性变化会触发重绘 | 尽量合并修改 |
| 使用 CSS 动画代替 JS 动画 | CSS 动画更高效 | @keyframes fade { ... } |
| 使用 will-change | 提前让浏览器优化元素 | will-change: transform |
| 避免使用 box-shadow border-radius动画 | 这些属性变化代价高 | 尽量静态使用 |

18.CSS优化?

代码层面:移除未使用样式,使用简写属性,避免过度嵌套,减少重排重绘,尽量用<link>而不是@import

其他层面:代码压缩(Webpack 的 css-minimizer-webpack-plugin),合并css文件减少请求,使用媒体查询优化移动端,异步加载首屏之外的css,cdn加速,压缩图片等等

19.哪些方式可以隐藏元素,区别?

display:none。 完全隐藏,不占空间,不能响应事件

visibility:hidden。 只是隐藏,还占空间

opacity:0 只是透明,还占空间,可以响应事件

直接移出页面

20.元素水平垂直居中的方式?

行内元素:text-align:center

其他:

flex布局:display: flex; justify-content: center; align-items: center;

grid布局:display: grid; place-items: center;

定位:

复制代码
.container {
  position: relative;
  height: 100vh;
}

.center {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

21.如何画一条0.5px的线?

复制代码
1.使用transform:scaleY(0.5)
<div class="line"></div>
.line {
  height: 1px;
  background: #000;
  transform: scaleY(0.5);
  transform-origin: top;
}

2.使用box-shadow模拟
.line {
  height: 1px;
  box-shadow: 0 0.5px 0 #000;
}

3.使用svg绘制
<svg width="100%" height="1">
  <line x1="0" y1="0.5" x2="100%" y2="0.5" stroke="#000" stroke-width="0.5"/>
</svg>

22.如何画一个三角形,原理是什么?

复制代码
.triangle-up {
    width: 0;
    height: 0;
    border-left: 50px solid transparent;
    border-right: 50px solid transparent;
    border-bottom: 100px solid #007bff; /* 三角形的颜色 */
}

当你给一个元素设置width: 0; height: 0;并且定义非透明的边框时,这些边框会在元素中心相交。如果四个边框中有三个是透明或隐藏的,而只有一个是有颜色的,那么有颜色的那个边框就会形成一个三角形

23.为什么 li与 li 元素之间有看不见的空白间隔,如何解决?

这个空白并不是 CSS 样式导致的,而是HTML 代码中的换行符或空格引起的。

解决:

1.删除html每个li中的空白

2.设置父元素font-size = 0

3.使用flex布局,gap = 0

24.精灵图(雪碧图)是什么,怎么使用?

通过将多个小图标或背景图像合并为一张大图,然后利用 CSS 的 background-position 属性来显示其中某一部分图像,从而减少 HTTP 请求次数,提高页面加载速度。

复制代码
.icon {
  background-image: url('sprites.png'); /* 精灵图路径 */
  background-repeat: no-repeat;
  display: inline-block;
}

.icon-home {
  width: 20px;  /* 小图宽度 */
  height: 20px; /* 小图高度 */
  background-position: 0 0; /* 小图在精灵图中的位置 */
}

.icon-search {
  width: 20px;
  height: 20px;
  background-position: -30px 0; /* 向左偏移30px */
}

25.rgba和opacity的透明效果有什么不同?

opacity:0.5

opacity是一个属性。opacity属性的值,可以被其子元素继承,给父级div设置opacity属性,那么所有子元素都会继承这个属性,并且,该元素及其继承该属性的所有子元素的所有内容透明度都会改变。

rgba(0,0,0,0.5)

rgba是一个属性值。rgba设置的元素,只对该元素的背景色有改变,并且,该元素的后代不会继承该属性。

补冲:rgba只是一个属性值,在background 里用改变背景色,在color里是改字体颜色,shadow里是改阴影色,不止是能够改元素的背景色,要看具体是在哪个属性上用

26.伪类和伪元素?

伪类(Pseudo-classes)

伪类用于选择处于特定状态的元素。例如,当用户将鼠标悬停在一个链接上时,或者表单输入获得焦点时。伪类使用单个冒号 : 开头。

常见伪类示例:
  • :hover - 当用户悬停在一个元素上时应用样式。
  • :active - 当点击一个元素时应用样式。
  • :focus - 当元素获得焦点时应用样式。
  • :nth-child(n) - 匹配属于其父元素的第 n 个子元素的元素。
  • :first-child:last-child - 分别匹配第一个和最后一个子元素。
  • :not(x) - 匹配不符合参数选择器 x 的元素。

示例:

复制代码
a:hover {
    color: red; /* 鼠标悬停在链接上时改变颜色 */
}
input:focus {
    border-color: blue; /* 输入框获得焦点时改变边框颜色 */
}
伪元素(Pseudo-elements)

伪元素用于创建不在文档树中的虚拟元素,并为其添加样式。伪元素通常使用双冒号 :: 开头,不过为了向后兼容,单冒号 : 仍然被广泛接受并支持。

常见伪元素示例:
  • ::before::after - 在选定元素的内容之前或之后插入生成的内容。
  • ::first-line - 应用于块级元素的第一行文本。
  • ::first-letter - 应用于块级元素的第一字母。
  • ::selection - 匹配用户选中的部分文字。

注意: 使用 ::before::after 伪元素时,通常需要结合 content 属性来指定要插入的内容。

示例:

复制代码
p::first-line {
    font-weight: bold; /* 第一行文字加粗 */
}

blockquote::before {
    content: open-quote; /* 在引用内容前插入开引号 */
}

blockquote::after {
    content: close-quote; /* 在引用内容后插入闭引号 */
}

27.CSS按需加载?

|-------------|----------------------|-------------------------|-------------------------------------|-----------------------------------------------------|---------------|
| 维度 | 原生媒体查询 | 动态插入 link/style | 构建时拆分+路由懒加载 | CSS-in-JS 模块 | SW 缓存+预加载 |
| 触发时机 | 媒体特征变化(打印、暗黑、屏幕尺寸) | 业务逻辑(点击、弹窗、路由) | 路由切换 / 组件挂载 | 组件渲染 | 路由切换前 |
| 技术实现 | <link media="..."> | createElement('link') | Webpack/Vite/Rollup 拆包 + import() | styled-components / emotion / vanilla-extract | Workbox 预缓存 |
| 首屏 CSS | 只加载命中条件的文件 | 不加载 | 只加载入口 chunk | 仅渲染所需组件样式 | 首次缓存后秒开 |
| 重复请求 | ❌ 浏览器自动去重 | ✅ 需手动记录 URL | ❌ 浏览器缓存 chunk | ❌ 运行时缓存 | ❌ SW 拦截 |
| FOUC 风险 | 低 | 需骨架屏 / onload | 低(同 chunk 加载) | 低(插入 <style> ) | 低 |
| 开发成本 | 最低(声明式) | 中等(需封装) | 低(零改动代码) | 低(与组件同文件) | 高(配置 SW) |
| 推荐场景 | 响应式、打印、主题 | 简单页面/弹窗 | SPA/MPA 通用 | React/Vue 组件库 | PWA 高性能需求 |

复制代码
/* 仅打印时下载此文件 */
<link rel="stylesheet" href="print.css" media="print" onload="this.media='all'">

/* 仅暗黑模式生效,浏览器会延迟下载 */
<link rel="stylesheet" href="dark.css" media="(prefers-color-scheme: dark)">

1.懒加载css文件

2.动态创建link