先上一段让你组件被全局断点拖垮的代码:
/* 媒体查询:只能根据视口宽度变化 */
@media (max-width: 768px) {
.card {
flex-direction: column;
padding: 16px;
}
}
组件是响应式了,但它是根据视口宽度的——不管这个组件被放到页面的哪个位置,只要屏幕宽度小于 768px,它就会变成纵向,即使它所在容器非常宽。
媒体查询的局限性:它只能看整个浏览器的窗口。你在布局里把卡片放进了一个 300px 的侧边栏,它还是会按视口宽度来决定自己的样式,而不是按它自己所在的容器宽度——结果卡片在 300px 的容器里显示横向布局。
容器查询 @container 让组件根据容器尺寸响应:
/* 定义父容器为可查询 */
.card-container {
container-type: inline-size;
container-name: card;
}
/* 容器查询:根据容器查询尺寸决定卡片样式 */
@container card (max-width: 400px) {
.card {
flex-direction: column;
padding: 16px;
}
}
组件不再看视口,而是看自己所在容器的查询尺寸。把它放到 300px 的侧边栏里,它会自动变成纵向;放到 800px 的主内容区,它又是横向。今天把容器查询的核心用法和实战场景一次讲清楚。
先搞清楚容器查询是什么
容器查询是 CSS 的新标准,让组件根据父容器的尺寸来响应,而不是根据视口。
| 对比 | 媒体查询@media | 容器查询@container |
|---|---|---|
| 参考对象 | 视口(浏览器窗口) | 父容器(最近的可查询祖先) |
| 应用范围 | 全局页面布局 | 组件自身样式 |
| 组件复用性 | 低(依赖上下文) | 高(独立于上下文) |
| 适用场景 | 页面级布局 | 组件级响应式 |
1. 基础用法:三步启动容器查询
第一步:定义可查询容器
.card-container {
container-type: inline-size; /* 基于容器 inline 轴尺寸 */
container-name: card; /* 给容器起个名字(可选) */
}
container-type: inline-size 表示只监听容器的 inline 轴尺寸变化(默认横排书写模式下通常是宽度),不监听 block 轴尺寸。这是最常用的模式,性能更好。
第二步:写容器查询
/* 方式一:用名字指定容器 */
@container card (max-width: 400px) {
.card {
flex-direction: column;
}
}
/* 方式二:不指定名字(找最近的可查询容器) */
@container (max-width: 400px) {
.card {
flex-direction: column;
}
}
第三步:组件样式独立
.card {
display: flex;
gap: 20px;
padding: 24px;
}
/* 容器查询只控制需要变化的部分 */
@container (max-width: 400px) {
.card {
flex-direction: column;
padding: 16px;
}
}
2. 实战场景
场景一:可复用卡片组件(最常用)
<div class="card-container card-container--wide">
<div class="card">
<img src="..." class="card-img" />
<div class="card-body">
<h3 class="card-title">标题</h3>
<p class="card-desc">描述文字...</p>
<button class="card-btn">按钮</button>
</div>
</div>
</div>
<div class="card-container card-container--narrow" style="width:280px;">
<div class="card">
<!-- 同样的卡片,放在窄容器里自动变成纵向 -->
</div>
</div>
.card-container {
container-type: inline-size;
container-name: card-container;
}
.card {
display: flex;
gap: 24px;
padding: 24px;
background: white;
border-radius: 12px;
}
.card-img {
width: 120px;
height: 120px;
flex-shrink: 0;
border-radius: 8px;
}
.card-body {
display: flex;
flex-direction: column;
justify-content: center;
}
.card-title { font-size: 1.25rem; }
.card-desc { color: #666; line-height: 1.6; }
/* 容器 inline 轴尺寸 < 400px 时变成纵向 */
@container card-container (max-width: 400px) {
.card {
flex-direction: column;
text-align: center;
}
.card-img {
width: 100%;
height: auto;
max-width: 200px;
margin: 0 auto;
}
.card-btn {
align-self: center;
}
}
场景二:响应式导航栏
.nav-container {
container-type: inline-size;
container-name: nav;
}
.nav {
display: flex;
gap: 16px;
align-items: center;
}
.nav-links {
display: flex;
gap: 12px;
}
.nav-hamburger {
display: none;
}
@container nav (max-width: 600px) {
.nav-links {
display: none;
}
.nav-hamburger {
display: block;
}
}
3. 容器查询 vs 媒体查询
| 场景 | 用媒体查询@media | 用容器查询@container |
|---|---|---|
| 页面整体布局(header/footer) | ✅ 适合 | ❌ 不适合 |
| 可复用组件(卡片、列表、面板) | ❌ 容易出问题 | ✅ 最适合 |
| 侧边栏适配 | ❌ 无法精确控制 | ✅ 完美适配 |
| 组件库开发 | ❌ 组件被上下文限制 | ✅ 组件独立响应 |
4. 三个容易踩的坑
坑一:容器必须处于可计算尺寸的布局上下文中
容器查询要求查询轴上的尺寸可被计算。并不一定要手写固定宽度,但至少要有可计算的布局约束(比如 flex、grid、width、max-width 等)。如果该轴尺寸始终无法确定,查询结果就可能不符合预期。
坑二:container-type: inline-size vs size 的性能差异
90% 的场景只关心宽度,用 inline-size 就够了。如果需要同时监听宽高变化,才用 size,但性能开销会更大。
坑三:容器查询不能应用在定义了容器查询的元素自身
/* ❌ 在同一个元素上定义 container 并查询自己 */
.card {
container-type: inline-size;
}
@container (max-width: 400px) {
.card { /* 不会生效!不能查自己 */ }
}
/* ✅ 在父容器定义,在子元素查询 */
.card-container {
container-type: inline-size;
}
@container (max-width: 400px) {
.card { /* 生效 */ }
}
5. 兼容性
- Chrome 105+、Firefox 110+、Safari 16+
- 主流现代浏览器支持良好,可生产使用
- 需要支持旧版浏览器时,用媒体查询做兜底
小结
- 容器查询:让组件根据父容器的查询尺寸响应,而不是视口
- 核心语法:container-type: inline-size + @container (max-width: 400px)
- 对比媒体查询:媒体查询管“页面布局”,容器查询管“组件样式”
- 最适合的场景:可复用组件库、卡片、面板、侧边栏内的组件
- 90% 场景用 container-type: inline-size,需要同时监听宽高时用 size
- 不能查询自己,必须在父容器定义容器,在子元素查询
一句话:容器查询是组件化时代的响应式方案——组件根据自己所在容器的查询尺寸决定样式,而不是被全局视口大小绑架。做组件库、写可复用组件,容器查询是必学技能。





