1. CSS3新特性
最常用的:
- 选择器:像
:nth-child让我能精准选中元素,不用加一堆class - 圆角和阴影:
border-radius和box-shadow让UI更精致 - transform:做位移、旋转、缩放动画
- 渐变:
linear-gradient实现漂亮的背景过渡
布局相关的:
- Flexbox:解决一维布局(导航栏、列表)
- Grid:解决二维布局(整体页面架构)
- 多列布局:做类报纸效果
- 媒体查询:实现响应式适配
特效相关的:
- 过渡和动画:让交互更丝滑
- 文字特效:
text-shadow做标题美化 - 滤镜:
filter: blur()实现毛玻璃
新单位:rem、vw/vh 让布局更灵活
2. 盒子模型
2.1 什么是盒子模型
在CSS中,每一个HTML元素都是一个"盒子" ,这个盒子由四个部分组成:
┌─────────────────────────────────┐
│ margin(外边距) │
│ ┌───────────────────────────┐ │
│ │ border(边框) │ │
│ │ ┌─────────────────────┐ │ │
│ │ │ padding(内边距) │ │ │
│ │ │ ┌───────────────┐ │ │ │
│ │ │ │ │ │ │ │
│ │ │ │ content │ │ │ │
│ │ │ │ (内容区) │ │ │ │
│ │ │ │ │ │ │ │
│ │ │ └───────────────┘ │ │ │
│ │ └─────────────────────┘ │ │
│ └───────────────────────────┘ │
└─────────────────────────────────┘
2.2 盒子模型的两种计算方式
标准盒子模型(W3C标准)
box-sizing: content-box; /* 默认值 */
计算公式:
- 元素总宽度 = width + padding + border
- 元素总高度 = height + padding + border
IE盒子模型(怪异模式)
box-sizing: border-box; /* 常用! */
计算公式:
- 元素总宽度 = width(包含padding和border)
- 元素总高度 = height(包含padding和border)
3. 对BFC的理解
3.1 它是什么?(Block Formatting Context)
BFC的全称是 Block Formatting Context,中文叫块级格式化上下文。
你可以把它想象成一个独立的小宇宙或者一个封闭的盒子。
在这个小宇宙里,无论里面的元素只在这个盒子内部产生影响,绝对不会跑到盒子外面去干扰别的元素。同样,外面的元素也伸不进手来影响盒子内部的东西。
3.2 它能干什么?(BFC解决了什么问题)
场景一:边距打架--边距“打架”(我想要两个分开,它们却贴在一起)
你看到的画面:
┌─────────┐
│ 盒子A │ ← 距离下方 20px
└─────────┘
(这里应该有 40px 空隙)
┌─────────┐
│ 盒子B │ ← 距离上方 20px
└─────────┘
实际发生的画面:
┌─────────┐
│ 盒子A │
└─────────┘
(只有 20px 空隙)
┌─────────┐
│ 盒子B │
└─────────┘
像什么?
两个人约好保持2米距离——
- 盒子A往后退了1米
- 盒子B往后退了1米
- 结果:两人之间还是只有1米,因为后退的距离重叠了
BFC怎么帮?
让盒子A或盒子B“退到不同的房间里”:
- 盒子A在房间1里退1米
- 盒子B在房间2里退1米
- 结果:两个房间的距离 = 1米 + 1米 = 2米
场景二:高度塌陷--爸爸不管孩子(我想要爸爸包住孩子,爸爸却缩水了)
你想要的画面:
┌───────────────────┐
│ ╔══════╗ ╔══════╗ │
│ ║孩子1 ║ ║孩子2 ║ │
│ ╚══════╝ ╚══════╝ │
└───────────────────┘
实际发生的画面(孩子浮动了):
╔══════╗ ╔══════╗
║孩子1 ║ ║孩子2 ║
╚══════╝ ╚══════╝
┌─────┐
│ 爸爸│ ← 缩成一条线,包不住孩子
└─────┘
像什么?
- 爸爸铺开一张野餐垫,两个孩子跑出去玩了(浮动)
- 爸爸以为孩子不在垫子上了,就把垫子卷起来了
- 结果:孩子在外面玩,爸爸缩成一团
BFC怎么帮?
告诉爸爸:“你是个负责的家长,不管孩子跑多远,你都得把垫子铺开罩住他们。”
- 爸爸触发BFC后,会主动计算:“哦,我孩子还在,我得把垫子撑开”
场景三:文字环绕--我想要文字在右边,文字却绕着走
你想要的画面:
┌──────┐ ┌────────────────┐
│ │ │ │
│ 图片 │ │ 文字内容 │
│ │ │ 应该在右边 │
└──────┘ │ 整整齐齐 │
└────────────────┘
实际发生的画面:
┌──────┐
│ │ ┌─────────────┐
│ 图片 │ │文字开始往上 │
│ │ │绕过来 像这 │
└──────┘ │样绕在图片 │
文字会 │旁边 不是 │
一直绕 │整齐的一列 │
下去 └─────────────┘
像什么?
- 左边有个圆形花坛(浮动图片)
- 你本来想在右边铺一条直直的路(文字内容)
- 结果路遇到花坛就拐弯,绕着花坛走
BFC怎么帮?
告诉右边的文字:“你是个有原则的区域,别绕着走,直接挡住花坛,形成一条直直的边界。”
- 文字触发BFC后,就不会“流”到花坛旁边,而是自觉地在右边形成一个矩形区域
3.3 怎么召唤BFC?(最简单的口诀)
不需要写 bfc: true;,而是用这些属性来“触发”它:
| 触发方式 | 怎么写 | 常用度 |
|---|---|---|
| overflow: hidden; | .box { overflow: hidden; } | ⭐⭐⭐ 最常用,副作用小 |
| display: flow-root; | .box { display: flow-root; } | ⭐⭐ 专门用来创建BFC,无副作用 |
| float: left/right; | .box { float: left; } | ⭐ 但会改变布局,慎用 |
| position: absolute; | .box { position: absolute; } | ⭐ 脱离文档流,慎用 |
| display: flex; | .box { display: flex; } | ⭐⭐ 现代布局常用 |
4. CSS选择器
4.1 常用选择器
/* 基础选择器 */
* /* 通用选择器 */
div /* 标签选择器 */
.class /* 类选择器 */
#id /* ID选择器 */
/* 组合选择器 */
div p /* 后代选择器 */
div > p /* 子元素选择器 */
div + p /* 相邻兄弟选择器 */
div ~ p /* 通用兄弟选择器 */
/* 属性选择器 */
[type="text"] /* 完全匹配 */
[class^="icon"] /* 以icon开头 */
[class$="-btn"] /* 以-btn结尾 */
[class*="btn"] /* 包含btn */
/* 伪类 */
:hover /* 鼠标悬停 */
:focus /* 获取焦点 */
:first-child /* 第一个子元素 */
:last-child /* 最后一个 */
:nth-child(n) /* 第n个 */
:not(selector)/* 排除 */
/* 伪元素 */
::before /* 元素前 */
::after /* 元素后 */
::first-line /* 第一行 */
4.2 优先级权重
!important > 行内样式 > ID > 类/属性 > 标签 > 通配符
∞ 1000 100 10 1 0
讲解:
- 权重可以累加:
#nav .item a= 100 + 10 + 1 = 111 - 相同权重:后面的覆盖前面的
- !important:最高优先级,慎用(难以维护)
5. 布局系统
5.1 浮动
float: left/right; /* 浮动 */
clear: both; /* 清除浮动 */
/* 清除浮动方法 */
.clearfix::after {
content: "";
display: block;
clear: both;
}
5.2 定位
position: static; /* 默认 */
position: relative; /* 相对自身 */
position: absolute; /* 绝对定位 */
position: fixed; /* 固定视口 */
position: sticky; /* 粘性定位 */
/* 偏移属性 */
top/right/bottom/left
z-index /* 层叠顺序 */
5.3 Flex布局(一维布局) ⭐
/* 容器属性 */
display: flex;
flex-direction: row/column; /* 主轴方向 */
flex-wrap: wrap; /* 换行 */
justify-content: flex-start/center/space-between; /* 主轴对齐 */
align-items: center; /* 交叉轴对齐 */
align-content: space-around; /* 多行对齐 */
gap: 10px; /* 间距 */
/* 项目属性 */
flex-grow: 1; /* 放大比例 */
flex-shrink: 1; /* 缩小比例 */
flex-basis: 0; /* 初始大小 */
flex: 1 1 0; /* 简写:把剩下的空间都占满 */
align-self: center; /* 单独对齐 */
order: 1; /* 排序 */
5.4 Grid布局(二维布局)
display: grid;
grid-template-columns: 1fr 1fr 1fr; /* 三列等宽 */
grid-template-rows: 100px auto; /* 两行 */
gap: 10px; /* 间距 */
/* 合并单元格 */
grid-column: 1 / 3; /* 占1-3列 */
grid-row: 1 / span 2; /* 占1行跨越2行 */
6. 重排和重绘
6.1 什么是重排和重绘
| 概念 | 比喻 | 本质 | 代价 |
|---|---|---|---|
| 重排 | 重新装修房子,敲墙改格局 | 重新计算位置、大小 | 💥 高 |
| 重绘 | 只给墙刷个新颜色 | 重新画外观样式 | ⚡ 低 |
核心关系:
- 重排 一定导致 重绘(格局变了,肯定要重新画)
- 重绘 不一定导致 重排(只是换个颜色,格局没变)
6.2 如何减少重排
批量操作样式
用 cssText 或 class 替代逐条修改
// 每次修改都触发重排
const el = document.getElementById('box');
el.style.width = '100px'; // 重排
el.style.height = '200px'; // 重排
el.style.margin = '10px'; // 重排
el.style.padding = '20px'; // 重排
// 触发了4次重排!
// 1.cssText
el.style.cssText = 'width: 100px; height: 200px; margin: 10px; padding: 20px;';
// 只触发1次重排
// 2.class
// CSS里预定义好
.new-style {
width: 100px;
height: 200px;
margin: 10px;
padding: 20px;
}
// JS里直接加class
el.classList.add('new-style'); // 1次重排
使用 transform
代替 top/left 做动画,把一些渲染任务交给显卡(GPU) 处理
/* 触发重排 */
.box {
position: absolute;
left: 100px;
top: 200px;
transition: left 0.3s; /* 动画时不断重排 */
}
/* 不触发重排 */
.box {
transform: translate(100px, 200px);
transition: transform 0.3s; /* GPU加速,丝滑 */
}
脱离文档流
让动画元素绝对定位
/* 让动画元素绝对定位,不影响其他人 */
.animate {
position: absolute; /* 或 fixed */
top: 0;
left: 0;
}
读写分离(避免强制重排)
// 交错读写 - 触发多次重排
el.style.width = '100px'; // 写
console.log(el.offsetWidth); // 读(强制重排)
el.style.height = '200px'; // 写
console.log(el.offsetHeight); // 读(强制重排)
el.style.margin = '10px'; // 写
console.log(el.offsetTop); // 读(强制重排)
// 读写分离批量写
el.style.width = '100px';
el.style.height = '200px';
el.style.margin = '10px';
// 读写分离批量读(只触发一次重排)
const width = el.offsetWidth;
const height = el.offsetHeight;
const top = el.offsetTop;
缓存布局信息
不要循环读 offset 属性
// 每次循环都读,每次读都触发重排
for(let i = 0; i < 100; i++) {
console.log(el.offsetHeight); // 读100次,重排100次
}
// 只读一次,后面用缓存
const height = el.offsetHeight;
for(let i = 0; i < 100; i++) {
console.log(height); // 用缓存,不触发重排
}
批量操作DOM
用 DocumentFragment 或先隐藏
错误示范:
// 每次添加都触发重排
const ul = document.getElementById('list');
for(let i = 0; i < 100; i++) {
const li = document.createElement('li');
li.textContent = `item ${i}`;
ul.appendChild(li); // 100次重排
}
正确姿势:
方法A:用DocumentFragment(虚拟容器)
const ul = document.getElementById('list');
const fragment = document.createDocumentFragment(); // 虚拟容器
for(let i = 0; i < 100; i++) {
const li = document.createElement('li');
li.textContent = `item ${i}`;
fragment.appendChild(li); // 操作虚拟DOM
}
ul.appendChild(fragment); // 1次重排
方法B:先用display:none隐藏
const ul = document.getElementById('list');
ul.style.display = 'none'; // 隐藏(1次重排)
// 批量添加
for(let i = 0; i < 100; i++) {
const li = document.createElement('li');
li.textContent = `item ${i}`;
ul.appendChild(li);
}
ul.style.display = 'block'; // 显示(1次重排)
// 总共2次重排,比100次好多了
用 visibility 替代 display(如果只是隐藏)
| 场景 | 用哪个 | 原因 |
|---|---|---|
| 元素要消失且不影响布局 | display: none | 触发重排,但别人会占位 |
| 元素要消失但保持占位 | visibility: hidden | 只重绘,不重排 |
/* 只重绘,不重排 */
.hidden {
visibility: hidden;
}
避免 table 布局
table 重排代价大
/* 避免 */
table {
display: table;
}
/* 推荐 */
div {
display: flex;
display: grid;
}
使用 will-change(提前告知浏览器优化)
/* 告诉浏览器这个元素要变了,提前优化 */
.animate {
will-change: transform, opacity;
}
7. 浏览器渲染过程
DOM树构建 → 2. CSSOM树构建 → 3. 渲染树构建 → 4. 布局(重排) → 5. 绘制(重绘)
↓ ↓ ↓ ↓ ↓
解析HTML 解析CSS 合并DOM+CSSOM 计算位置大小 填充像素
8. 响应式--媒体查询
/* 媒体查询 */
@media (max-width: 768px) {
/* 手机 */
}
@media (min-width: 769px) and (max-width: 1024px) {
/* 平板 */
}
/* 单位 */
% /* 百分比 */
vw/vh /* 视口宽高 */
rem /* 相对于根元素 */
em /* 相对于父元素 */
9. Sass和Less的区别
9.1 先看代码长得什么样
Less 写法(像普通CSS)
@蓝色: #1890ff; // 变量用 @
@字体大小: 14px;
.button {
color: @蓝色; // 直接用变量
font-size: @字体大小;
}
Sass 写法(也像普通CSS)
$蓝色: #1890ff; // 变量用 $
$字体大小: 14px;
.button {
color: $蓝色; // 直接用变量
font-size: $字体大小;
}
唯一的区别: Less用 @,Sass用 $。就这么简单!
9.2 核心区别:一个像"复制粘贴",一个像"编程"
Less:像"高级复制粘贴"
// 定义一段样式
.圆角(@半径) {
border-radius: @半径;
}
// 需要的地方直接"复制"过来
.button {
.圆角(5px); // 就是把上面的代码贴到这里
}
.card {
.圆角(10px); // 再贴一次
}
Sass:像"写程序"
// 定义
@mixin 圆角($半径) {
border-radius: $半径;
}
// 调用
.button {
@include 圆角(5px); // 像在"执行"一段程序
}
// Sass还能这样(Less做不到)
@for $i from 1 through 3 {
.col-#{$i} {
width: $i * 100px;
}
}
// 自动生成:
// .col-1 { width: 100px; }
// .col-2 { width: 200px; }
// .col-3 { width: 300px; }
9.3 总结:该怎么选?
| 你的情况 | 推荐 | 原因 |
|---|---|---|
| 刚接触、只想让CSS更好写 | Less | 像CSS一样简单,半小时上手 |
| 团队项目、需要严谨规范 | Sass | 功能强大,大厂都在用 |
| 写简单页面、不用复杂逻辑 | Less | 够用就行 |
| 写组件库、UI框架 | Sass | 循环判断很有用 |
一句话记法:
- Less = CSS plus(CSS基础上加点料)
- Sass = 写CSS像写代码(有逻辑、能计算)
10. CSS的常见单位
10.1 px像素
是什么?
绝对单位,屏幕上的1个点。
代码示例
.box {
width: 200px; /* 固定宽200像素 */
font-size: 16px; /* 固定字16像素 */
border: 1px solid #000; /* 1像素边框 */
margin: 20px; /* 固定间距20像素 */
}
特点
✅ 优点: 稳定、精确、好控制
❌ 缺点: 不随屏幕变化,大屏上显小,小屏上显大
适用场景
- 边框(
border: 1px) - 小图标尺寸
- 固定宽度的侧边栏
- 圆角(
border-radius: 8px)
10.2 百分比 %
是什么?
相对单位,相对于父元素的相同属性。
代码示例
.parent {
width: 500px;
height: 300px;
font-size: 20px;
}
.child {
width: 50%; /* = 250px (父宽度的50%) */
height: 50%; /* = 150px (父高度的50%) */
font-size: 80%; /* = 16px (父字体的80%) */
margin-left: 10%; /* = 50px (父宽度的10% - 注意不是高度!) */
padding: 5%; /* = 25px (父宽度的5% - 也不是高度!) */
}
特点
✅ 优点: 随父元素变化,实现响应式
❌ 缺点: 多层嵌套时计算复杂
两个大坑(面试常考)
/* 坑1:margin/padding的%永远相对于父元素的宽度 */
.parent { width: 500px; height: 1000px; }
.child { margin-top: 10%; } /* = 50px,不是100px! */
/* 坑2:父元素没高度时,height:%失效 */
.parent { } /* 没有设置高度 */
.child { height: 50%; } /* 不生效! */
适用场景
- 响应式布局(
width: 100%) - 图片自适应(
max-width: 100%) - 流体布局
10.3 em
是什么?
相对单位,相对于当前元素或父元素的字体大小。
代码示例
/* 情况1:相对于父元素 */
.parent {
font-size: 20px;
}
.child {
font-size: 0.8em; /* = 16px (20 * 0.8) */
width: 2em; /* = 32px (相对于自己的font-size 16px) */
padding: 1em; /* = 16px (相对于自己的font-size) */
}
/* 情况2:em会叠加 */
html { font-size: 16px; } /* 16px */
div { font-size: 1.5em; } /* 24px (16*1.5) */
div p { font-size: 0.8em; } /* 19.2px (24*0.8) */
div p span { font-size: 0.5em; } /* 9.6px (19.2*0.5) */
特点
✅ 优点: 组件内部比例协调,随文字大小整体缩放
❌ 缺点: 会叠加,层级深了难以预测
适用场景
- 按钮内边距(让按钮大小随文字变化)
- 卡片内部间距
- 组件库开发
10.4 rem
是什么?
相对单位,相对于根元素(html) 的字体大小。
代码示例
/* 设置根元素字体 */
html {
font-size: 16px; /* 1rem = 16px */
}
/* 使用rem */
.title {
font-size: 2rem; /* = 32px */
margin: 1.5rem; /* = 24px */
}
.content {
font-size: 1rem; /* = 16px */
width: 10rem; /* = 160px */
padding: 1rem 2rem; /* = 16px 32px */
}
/* 移动端适配:改变根字体 */
@media (max-width: 375px) {
html {
font-size: 14px; /* 小屏上整体缩小 */
}
}
特点
✅ 优点: 全局统一、可控、不叠加
❌ 缺点: 所有元素都听根元素的
适用场景
- 全局字体大小
- 页面主要间距
- 移动端适配
10.5 vw / vh
是什么?
视口单位,相对于浏览器窗口。
| 单位 | 含义 |
|---|---|
| vw | 视口宽度的1% |
| vh | 视口高度的1% |
代码示例
/* 全屏区域 */
.hero {
width: 100vw; /* 满屏宽 */
height: 100vh; /* 满屏高 */
}
/* 随屏幕缩放的文字 */
h1 {
font-size: 8vw; /* 屏幕越宽字越大 */
/* 屏幕宽375px → 30px */
/* 屏幕宽414px → 33px */
}
/* 正方形(宽高相等) */
.square {
width: 30vw;
height: 30vw; /* 用vw保证宽高相等 */
}
/* 居中弹窗 */
.modal {
width: 80vw;
height: 60vh;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
特点
✅ 优点: 直接跟随视口,适合全屏和响应式
❌ 缺点: 手机上有工具栏,100vh可能超出
适用场景
- 全屏背景图/轮播图
- 大标题文字
- 弹窗、浮层
10.6 对比总结表
| 单位 | 参考对象 | 会不会叠加 | 适用场景 | 一句话记法 |
|---|---|---|---|---|
| px | 无 | 不叠加 | 边框、小图标、固定尺寸 | 打死不变 |
| % | 父元素 | 会叠加 | 流体布局、响应式 | 听爸爸的话 |
| em | 父/自身 | 会叠加 | 按钮、卡片内部 | 听爹的话 |
| rem | 根元素 | 不叠加 | 全局字体、间距 | 听老祖宗的话 |
| vw/vh | 视口 | 不叠加 | 全屏、大标题 | 看窗户大小 |