理解 flex-grow、flex-shrink 和 flex-basis

Avatar of Robin Rendle
Robin Rendle

DigitalOcean 为您旅程的每个阶段提供云产品。 立即开始使用 价值 200 美元的免费积分!

当您将 CSS 属性应用于元素时,幕后会发生很多事情。 例如,假设我们有一些像这样的 HTML 代码

<div class="parent">
  <div class="child">Child</div>
  <div class="child">Child</div>
  <div class="child">Child</div>
</div>

然后我们编写一些 CSS 代码...

.parent {
  display: flex;
}

从技术上讲,这并非我们上面编写的那一行 CSS 代码应用的唯一样式。 事实上,这里将对 .child 元素应用一大堆属性,就好像我们自己编写了这些样式一样

.child {
  flex: 0 1 auto; /* Default flex value */
}

这很奇怪!为什么这些元素即使我们没有编写该代码也会应用这些额外的样式? 嗯,这是因为一些属性具有默认值,然后我们打算覆盖这些默认值。 而且,如果我们在编写 CSS 时不知道正在应用这些样式,那么我们的布局就会变得非常混乱且难以管理。

上面的 flex 属性被称为 CSS 简写属性。 实际上,这个属性是在同时设置三个独立的 CSS 属性。 所以,我们上面编写的代码与编写以下代码相同

.child {
  flex-grow: 0;
  flex-shrink: 1;
  flex-basis: auto;
}

所以,简写属性将一堆不同的 CSS 属性捆绑在一起,以方便一次编写多个属性,就像 background 属性一样,我们可以编写这样的代码

body {
  background: url(sweettexture.jpg) top center no-repeat fixed padding-box content-box red;                   
}

我尽量避免使用简写属性,因为它们可能会非常混乱,而且我经常倾向于编写长格式版本,因为我的大脑无法解析长长的属性值行。 但对于 flexbox 而言,建议使用简写,这很奇怪……也就是说,直到您了解到 flex 属性正在进行大量工作,并且它的每个子属性都与其他属性相互作用。

此外,默认样式是一件好事,因为我们大多数情况下不需要知道这些 flexbox 属性的作用。 例如,当我使用 flexbox 时,我倾向于编写类似这样的代码

.parent {
  display: flex;
  justify-content: space-between;
}

我甚至不需要关心子元素或对它们应用了哪些样式,这很棒! 在这种情况下,我们将子项并排对齐,然后在它们之间均匀地间距。 两行 CSS 代码就能为您提供强大的功能,这是 flexbox 和这些继承样式最棒的地方——如果您只是想始终做同样的事情,您不必了解所有底层的复杂性。 它非常聪明,因为所有这些复杂性都隐藏在视线之外。

但是,如果我们想了解 flexbox(包括 flex-growflex-shrinkflex-basis 属性)是如何工作的? 我们用它们可以做哪些很酷的事情?

直接访问 CSS-Tricks 年鉴 就可以了!

开个玩笑。 让我们从一个简化的快速概述开始,然后回到应用于子元素的默认 flex 属性

.child {
  flex: 0 1 auto;
}

这些默认样式告诉子元素如何拉伸和扩展。 但是,每当我看到它被使用或被覆盖时,我发现将这些简写属性这样理解很有帮助

/* This is just how I think about the rule above in my head */

.child {
  flex: [flex-grow] [flex-shrink] [flex-basis];
}

/* or... */

.child {
  flex: [max] [min] [ideal size];
}

第一个值是 flex-grow,它被设置为 0,因为默认情况下,我们不希望元素扩展(大多数情况下)。 相反,我们希望每个元素都取决于它内部内容的大小。 以下是一个示例

.parent { 
  display: flex; 
}

我在上面的每个 .child 元素中都添加了 contenteditable 属性,这样您就可以单击它并输入更多内容。 看一下它是如何响应的? 这是 flexbox 项目的默认行为:flex-grow 设置为 0,因为我们希望元素根据它内部的内容进行增长。

但是! 如果我们要将 flex-grow 属性的默认值从 0 更改为 1,像这样...

.child {
  flex: 1 1 auto;
}

然后所有元素将占据 .parent 元素的相等部分,但只有当它们的内容长度相同时

这与编写以下代码完全相同...

.child {
  flex-grow: 1;
}

…并忽略其他值,因为这些值已经默认设置好了。 我认为,在我开始使用灵活布局时,这让我困惑了很长时间。 我会看到添加了 flex-grow 的代码,并想知道其他样式来自哪里。 这就像一个让我抓狂的谋杀悬案,我无法弄清楚。

现在,如果我们想让其中一个元素比其他元素增长更多,我们只需要执行以下操作

.child-three {
  flex: 3 1 auto;
}

/* or we could just write... */

.child-three {
  flex-grow: 3;
}

即使 flexbox 已经进入了浏览器十年,这段代码看起来也很奇怪吗? 对于我来说确实如此。 在阅读简写代码时,我需要额外的脑力来思考“最大值、最小值、理想尺寸”,但这随着时间的推移会变得更容易。 无论如何,在上面的示例中,前两个子元素将占用比例相同的空间,但第三个元素将尝试增长到其他元素的三倍空间。

现在,这里事情变得奇怪了,因为这一切都取决于子元素的内容。 即使我们将 flex-grow 设置为 3,就像我们在上面的示例中所做的那样,然后添加更多内容,布局也会做一些奇怪的事情,比如这样

第二列现在占据了太多空间! 我们稍后会回到这里,但现在,记住 flex 项的内容会影响 flex-growflex-shrinkflex-basis 的协同工作方式非常重要。

好了,现在是 flex-shrink。 请记住,这是简写中的第二个值

.child {
  flex: 0 1 auto; /* flex-shrink = 1 */
}

flex-shrink 告诉浏览器元素的最小尺寸应该是多少。 默认值为 1,这意味着“始终占用相同数量的空间”。 但是! 如果我们将该值设置为 0,像这样

.child {
  flex: 0 0 auto;
}

…那么我们现在告诉这个元素根本不要缩小。 保持相同的大小,你这个该死的元素! 基本上这就是 CSS 代码要表达的意思,它也会完全按照指示去做。 我们稍后会回到这个属性,等我们看一下简写中的最后一个值。

flex-basis 是默认情况下添加到 flex 简写中的最后一个值,它是我们告诉元素保持理想尺寸的方式。 默认情况下,它被设置为 auto,这意味着“使用我的高度或宽度”。 所以,当我们将父元素设置为 display: flex 时...

.parent {
  display: flex;
}

.child {
  flex: 0 1 auto;
}

在浏览器中,我们会默认获得以下结果

请注意,默认情况下,所有元素的宽度都是它们内容的宽度? 这是因为 auto 表示元素的理想大小由其内容定义。 为了让所有元素占据父元素的整个空间,我们可以将子元素设置为 width: 100%或者我们可以将 flex-basis 设置为 100%或者我们可以将 flex-grow 设置为 1

这些都有道理吗? 这很奇怪,对吧! 仔细想想就会明白。 这些简写值中的每一个都会影响其他的值,这就是为什么建议首先编写这个简写,而不是彼此独立地设置这些值。

好的,继续。 当我们编写类似这样的代码时...

.child-three {
  flex: 0 1 1000px;
}

我们在这里告诉浏览器将 flex-basis 设置为 1000px,或者说“请、请、请尽可能占据 1000px 的空间”。 如果不可能,那么元素将根据其他元素的比例占据那么多的空间。

您可能会注意到,在较小的屏幕上,第三个元素实际上并没有达到 1000px! 这是因为它只是一个建议。 我们仍然应用了 flex-shrink,它告诉元素缩小到与其他元素相同的大小。

此外,向其他子元素添加更多内容也会在这里产生影响

现在,如果我们想阻止这个元素完全缩小,我们可以编写类似这样的代码

.child-three {
  flex: 0 0 1000px;
}

请记住,flex-shrink 这里面的第二个值,通过将其设置为 0,我们实际上是在说“永远不要缩小,你这个混蛋”。 这样它就不会缩小了。 这个元素甚至会超出父元素,因为它永远不会比 1000px 宽更小

现在,如果我们将 flex-wrap 设置为父元素,所有这些都会发生变化

.parent {
  display: flex;
  flex-wrap: wrap;
}

.child-three {
  flex: 0 0 1000px;
}

我们会看到以下结果

这是因为,默认情况下,flex 项将尝试适应一行,但 flex-wrap: wrap 会完全忽略这一点。 现在,如果这些 flex 项无法适应相同的空间,它们会换行。


无论如何,这只是 flex 属性相互碰撞的一些方式,以及为什么了解这些属性在底层的运作方式如此重要。 这些属性中的每一个都可能影响其他属性,如果您不了解一个属性是如何工作的,那么您基本上就不了解任何一个属性是如何工作的——这确实让我在开始深入研究之前感到困惑!

但总而言之

  • 尝试使用 flex 简写
  • 这样做时,请记住最大值、最小值和理想尺寸
  • 请记住,元素的内容也会影响这些值之间的协同工作方式。