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) |
| 尺寸计算 | width
和height
仅指内容区域 | width
和height
包括内容区域、内边距和边框 |
| 总宽度计算 | 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)会合并为一个边距
如:
- 相邻兄弟元素之间的边距重叠
当两个或多个相邻的块级元素垂直排列时,它们的上下边距可能会重叠。例如,如果第一个元素有margin-bottom: 20px;
,第二个元素有margin-top: 30px;
,那么它们之间的实际间距将是30px,而不是50px。
- 父元素与第一个或最后一个子元素之间的边距重叠
当一个元素包含在另一个元素中时,如果父元素没有边框(border)、内边距(padding)或行内内容来分隔它们,父元素的上外边距(margin-top)或下外边距(margin-bottom)会与子元素的上外边距或下外边距发生重叠
- 空块级元素的边距重叠
对于没有任何内容、填充(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)
;
不同的媒体用不同的样式
- 流式布局:
em
、rem
、 百分比(Bootstrap 的 12 栅格)等相对单位设定; vw
和vh
方案- 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)
|
| 避免频繁读取布局属性 | 如 offsetTop
、clientWidth
会触发重排 | 缓存这些值 |
| 使用 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