在跨设备网页开发里,最让人头疼的问题之一就是页面元素在各种屏幕上比例完全不对。用固定像素写的宽度,到了小手机上会超出视口,到了大显示器上又显得格外空旷。解决这个问题的核心思路,是放弃写死尺寸,转而使用百分比宽度与flex-grow来让元素自己适应父容器与剩余空间。

为什么固定宽度会导致比例失衡
当我们给一个容器设置width: 800px,它的含义是无论外面屏幕是375像素的手机还是1920像素的桌面,这个盒子永远是800像素宽。在手机上,浏览器会出现横向滚动条,或者元素被强行压缩导致文字重叠;在桌面上,800像素可能只占中间一小条,两边大片留白。这种失衡本质上是布局单位没有跟随视口变化。
百分比宽度以父元素为基准,比如width: 50%表示占父级一半。如果父级本身也是百分比或视口单位,那么这一半就会随着屏幕大小自动伸缩。不过百分比只能处理明确分配的比例,当父容器有多余空间或空间不足时,单靠百分比无法智能分配,这就需要flex-grow出场。
百分比宽度的正确用法
百分比宽度适合用来划分页面的大区块。比如左右两栏布局,左栏占30%,右栏占70%,它们会始终维持三比七的比例,不会因设备改变而崩坏。需要注意的是,百分比是相对于父容器内容区计算的,如果父容器有padding且用了默认的content-box,实际可用宽度会变小,建议配合box-sizing: border-box使用。
下面是一个简单的两栏百分比布局示例,我们在代码里用转义方式写出标签名,避免被解析为真实节点:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<style>
* { box-sizing: border-box; }
.wrap { width: 100%; }
.left { width: 30%; float: left; background: #eee; }
.right { width: 70%; float: left; background: #ddd; }
</style>
</head>
<body>
<div class="wrap">
<div class="left">侧边</div>
<div class="right">主内容</div>
</div>
</body>
</html>
上面的代码在任意宽度下都保持三比七,但如果我们希望右栏在左栏内容很少时自动撑满,百分比就做不到,因为左栏永远占30%。这时要用弹性盒子。
flex-grow如何分配剩余空间
flex-grow是flex布局中的一个属性,它定义弹性项在有剩余空间时放大的比例。默认值是0,即不放大。如果多个子项都设置了flex-grow: 1,那么剩余空间会被均分;如果分别是1和2,则按一比二分配。它解决的是“百分比分完之后剩下的空间给谁”的问题。
举例来说,一个flex容器宽1000像素,两个子项基础宽度各400像素,剩下200像素。若第一项flex-grow为1,第二项为1,则各加100像素;若第一项为0,第二项为1,则第二项独享200像素。这样就能在不同屏幕上让主要内容区自然伸展,而不会留下空洞。
.container {
display: flex;
width: 100%;
}
.side {
flex-basis: 200px;
flex-grow: 0;
flex-shrink: 0;
}
.main {
flex-basis: 0;
flex-grow: 1;
}
在上面样式中,side固定200像素不伸缩,main的flex-basis为0且flex-grow为1,意味着它会吃掉所有剩余宽度。无论屏幕是320还是1440,侧边栏不变,主区自适应,比例自然协调。
二者结合的实际方案
真实项目里通常不会只用其中一种。常见做法是:用百分比宽度划定页面级大框架,比如内容区最大宽1200像素居中,内部再用flex-grow分配卡片之间的空隙。例如商品列表,每个卡片用flex-basis: 30%加flex-grow: 1,一行三张,屏幕变窄时自动变一行两张,且间距均匀。
也要小心误区:flex-grow只会分配“剩余空间”,如果子项总基础宽度已经超过父级,就不会有剩余,此时要搭配flex-shrink控制收缩,或用媒体查询在极小屏改为纵向排列。另外,flex-grow放大的只是宽度,内部文字不会等比变大,需要另外用clamp()或相对单位调字号。
<div class="list"> <div class="card">商品一</div> <div class="card">商品二</div> <div class="card">商品三</div> </div>
.list { display: flex; gap: 16px; }
.card {
flex-basis: 30%;
flex-grow: 1;
background: #f5f5f5;
padding: 12px;
}
@media (max-width: 600px) {
.list { flex-direction: column; }
.card { flex-basis: auto; }
}
这段示例先让卡片在宽屏上按比例伸展,到了600像素以下改为竖排,彻底避免比例失衡。通过百分比定骨架、flex-grow填剩余、媒体查询做兜底,就能稳妥应对绝大多数设备的显示差异。