树欲静而风不止
叙述前端的戎码生涯

从 @media 到 @container 组件化的响应式

先上一段让你组件被全局断点拖垮的代码:

/* 媒体查询:只能根据视口宽度变化 */
@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
  • ‎不能查询自己,必须在父容器定义容器,在子元素查询

一句话:容器查询是组件化时代的响应式方案——组件根据自己所在容器的查询尺寸决定样式,而不是被全局视口大小绑架。做组件库、写可复用组件,容器查询是必学技能。

赞(0)
未经允许不得转载:OveUI » 从 @media 到 @container 组件化的响应式
分享到