容器查询(@container)无法管理状态,因其无记忆、无副作用、不支持异步与多组件差异化响应;需用js状态机(如xstate)处理语义化尺寸事件、业务逻辑和状态同步。

容器查询(@container)本身不依赖 JS,但当你需要在「组件级尺寸变化」触发复杂状态切换(比如从“紧凑态”切到“展开态”再进“编辑态”)时,纯 CSS 无法维护状态栈、处理异步条件或协调多个组件联动——这时候必须用 JS 状态机补位。
为什么不能只靠 @container 做状态管理
CSS 容器查询只响应尺寸阈值,它没有记忆、没有副作用、也不能阻塞或延迟响应。比如一个卡片组件在宽度 320px 时隐藏摘要,在 480px 时显示摘要但折叠详情,在 640px 时展开全部——这看似是三个断点,但实际交互中用户可能快速拖拽容器宽度,导致样式反复闪动、摘要刚展开又被收起。CSS 没法判断“用户正有意展开”,也没法等动画结束才更新内部按钮的 aria-expanded 属性。
-
@container规则里不能写 JavaScript 行为,也无法读取组件当前是否处于加载中、校验失败等业务状态 - 多个同类型组件共享同一套容器查询规则时,无法差异化响应:A 卡片正在编辑,B 卡片只是浏览,但它们的容器尺寸一致 → CSS 会一视同仁地应用相同样式
- 容器尺寸变化和 DOM 更新存在时序差:ResizeObserver 回调比样式计算快,JS 可以提前预判并加锁,CSS 只能被动响应
ResizeObserver + XState 实现轻量状态机
别手写状态机。用 XState(v5+)定义明确的状态节点和转移条件,把容器尺寸映射成事件输入。关键不是监听“多少像素”,而是监听“是否跨过语义边界”。
- 在组件挂载时初始化
ResizeObserver,但只上报width(高度通常不影响布局语义) - 将观察到的宽度映射为离散事件:
WIDTH_TINY()、<code>WIDTH_COMPACT(320–479px)、WIDTH_NORMAL(>=480px) - 状态机内定义 guard 条件,例如:
context: { isEditing: false }时,WIDTH_NORMAL才触发expandDetails动作;否则保持compact状态 - 每次状态变更后,用
element.setAttribute('data-layout-state', state.value)同步到 DOM,让 CSS 通过[data-layout-state="expanded"]精准控制样式
示例片段:
const layoutMachine = createMachine({
context: { width: 0, isEditing: false },
initial: 'tiny',
states: {
tiny: {
on: { WIDTH_COMPACT: 'compact' }
},
compact: {
on: {
WIDTH_NORMAL: [{ target: 'normal', cond: 'canExpand' }],
EDIT_START: 'editing'
}
},
normal: {
on: { WIDTH_COMPACT: 'compact' }
},
editing: {
on: { EDIT_END: 'compact' }
}
}
});
与容器查询共存时的样式协作方式
CSS 容器查询负责“静态样式层”,JS 状态机负责“动态行为层”,二者通过 data- 属性桥接。不要在容器查询里重复定义断点逻辑,更不要用 JS 直接改 style.width。
- 容器查询只做基础流体适配:
@container (min-width: 320px) { .card-summary { display: block; } } - 所有需要状态感知的样式(如展开动画、禁用态遮罩、焦点轮廓偏移)全走
[data-layout-state]选择器 - 避免在 JS 中调用
getComputedStyle判断当前布局——那会破坏分离原则;状态机输出就是唯一事实源 - 若组件需响应父容器
container-type: inline-size以外的维度(比如内容行数、字体加载完成),这些只能由 JS 触发事件交由状态机处理
真正难的不是监听尺寸,而是定义哪些尺寸变化值得响应、哪些该被忽略、哪些要合并延迟。状态机的价值不在“多酷”,而在把模糊的“自适应”变成可测试、可回溯、可协作的明确状态流转。别让容器查询替你做决策,让它只管渲染;让 JS 状态机替你记住上下文,哪怕窗口被缩放到 1px 宽又拉回来,组件依然知道刚才自己在编辑什么。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











