Flexbox:那个灵活的盒子有多大?
已发表: 2022-03-10这是我关于 Flexbox 的系列文章的第三部分。 在过去的两篇文章中,我们研究了创建 flex 容器时会发生什么,并探索了它在 Flexbox 中的对齐方式。 这次我们来看看尺码。 我们如何控制弹性项目的大小,浏览器在控制大小时会做出哪些选择?
弹性项目的初始显示
如果我有一组项目,其中包含可变长度的内容,并将其父项设置为display: flex
,则项目将显示为一行并在该轴的开始处排列。 在下面的示例中,我的三个项目的内容很少,并且能够将每个项目的内容显示为一条完整的线。 由于flex-grow
的初始值为0
,因此项目不会增长到 flex 容器的末尾有空间,不要增长。

如果我向这些项目添加更多文本,它们最终会填满容器,并且文本开始换行。 这些框被分配了容器中的一部分空间,该空间对应于每个框中的文本数量——文本字符串较长的项目被分配更多空间。 这意味着当隔壁的项目只包含一个单词时,我们不会得到一个包含大量文本的又高又瘦的列。

如果您曾经使用过 Flexbox,那么您可能对这种行为很熟悉,但也许您想知道浏览器是如何工作的,就像您查看多个现代浏览器一样,您会发现它们都在做同样的事情。 这归结于规范中制定了诸如此类的细节,确保任何在新浏览器或其他用户代理中实现 Flexbox 的人都知道该计算应该如何工作。 我们可以使用规范为自己找到这些信息。
CSS 内部和外部尺寸规范
在查看 Flexbox 规范中有关大小调整的任何内容时,您很快就会发现,您需要的很多信息都在另一个规范中——CSS Intrisnic 和 Extrinsic Sizing。 这是因为我们使用的尺寸概念并不是 Flexbox 独有的,就像对齐属性不是 Flexbox 独有的一样。 但是,对于这些尺寸结构如何在 Flexbox 中使用,您需要查看 Flexbox 规范。 感觉有点像来回跳跃,所以我将在这里总结一些关键定义,我将在本文的其余部分使用它们。
首选尺寸
框的首选大小是由width
或height
定义的大小,或者是inline-size
和block-size
这些属性的逻辑别名。 通过使用:
.box { width: 500px; }
或逻辑别名inline-size
:
.box { inline-size: 500px; }
你说你希望你的盒子是 500 像素宽,或内联方向 500 像素。
最小内容大小
min-content size 是一个盒子在不引起溢出的情况下可以达到的最小尺寸。 如果您的盒子包含文本,那么将采取所有可能的软包装机会。
最大内容大小
最大内容大小是盒子可以容纳内容的最大大小。 如果该框包含没有格式的文本以将其拆分,则它将显示为一个完整的长字符串。
弹性项目主要尺寸
弹性项目的主要尺寸是它在主要维度中的尺寸。 如果你连续工作——用英语——那么主要的尺寸就是宽度。 在英文的列中,主要的尺寸是高度。
项目还具有最小和最大主尺寸,由它们在主尺寸上的min-width
或min-height
定义。
计算弹性项目的大小
现在我们已经定义了一些术语,我们可以看看我们的弹性项目是如何调整大小的。 flex
属性的初始值如下:
-
flex-grow: 0
-
flex-shrink: 1
-
flex-basis: auto
flex-basis
是计算尺寸的东西。 如果我们将flex-basis
设置为0
并将flex-grow
设置为 1,那么我们所有的盒子都没有起始宽度,因此 flex 容器中的空间被平均分配,为每个项目分配相同数量的空间。
参见 Pen Smashing Flexbox 系列 3: flex: 1 1 0; 作者:Rachel Andrew (@rachelandrew) 在 CodePen 上。
而如果flex-basis
是auto
和flex-grow: 1
,则仅分配备用空间,同时考虑到内容的大小。
请参阅 Rachel Andrew (@rachelandrew) 在 CodePen 上的 Pen Smashing Flexbox Series 3:flex: 1 1 auto short text。
在没有多余空间的情况下,例如当我们的内容多于一行时,就没有空间可以分发。
请参阅 Rachel Andrew (@rachelandrew) 在 CodePen 上的 Pen Smashing Flexbox 系列 3:flex: 1 1 auto long text。
这向我们表明,如果我们想知道 Flexbox 是如何计算出我们盒子的大小,那么弄清楚auto
的含义是非常重要的。 auto
的值将是我们的起点。
定义自动
当auto被定义为 CSS 中的某个值时,它将在该上下文中具有非常具体的含义,值得一看。 正如规范编辑 Fantasai 的这篇演讲所解释的那样,CSS 工作组花费了大量时间来弄清楚 auto 在任何情况下的含义。
我们可以在规范中找到有关auto
用作flex-basis
时的含义的信息。 上面定义的术语应该有助于我们剖析这个陈述。
“当在 flex 项目上指定时,auto 关键字检索主要尺寸属性的值作为使用的 `flex-basis`。 如果该值本身是 auto,那么使用的值就是 `content`。”
因此,如果我们的flex-basis
是auto
,Flexbox 会查看定义的主要尺寸属性。 如果我们给任何弹性项目一个width
,我们就会有一个主尺寸。 在下面的示例中,所有项目的宽度都是 110px,因此这被用作主要尺寸,因为 flex-basis 的初始值为 auto。

请参阅 Rachel Andrew (@rachelandrew) 在 CodePen 上的 Pen Smashing Flexbox Series 3: flex items with a width。
然而,我们最初的例子有没有宽度的项目,这意味着它们的主要尺寸是auto
的,所以我们需要进入下一句,“如果该值本身是自动的,那么使用的值就是content
。”
我们现在需要查看规范中关于content
关键字的内容。 这是您可以为您的flex-basis
使用(在支持浏览器中)的另一个值,例如:
.item { flex: 1 1 content; }
规范定义content
如下:
“指示基于弹性项目内容的自动大小。 (它通常相当于最大内容大小,但需要调整以处理纵横比、固有大小约束和正交流”
在我们的示例中,对于包含文本的弹性项目,我们可以忽略一些更复杂的调整并将content
视为最大内容大小。
所以这就解释了为什么当我们在每个项目中有少量文本时,文本不会换行。 flex 项目是自动调整大小的,所以 Flexbox 正在查看它们的最大内容大小,项目以该大小适合它们的容器,然后工作就完成了!
故事并没有到此结束,因为当我们添加更多内容时,框不会保持在最大内容大小。 如果他们这样做了,他们会打破弹性容器并导致溢出。 一旦它们填满容器,内容就会开始包装,并且项目会根据其中的内容变得不同大小。
解决灵活的长度
正是在这一点上,规范变得相当复杂,但是,需要执行的步骤如下:
首先,将所有项目的主要尺寸相加,看看它是否大于或小于容器中的可用空间。
如果容器大小大于总大小,我们将关心flex-grow
因素,因为我们有增长空间。

如果容器大小小于总大小,那么我们将关心flex-shrink
因子,因为我们需要收缩。

冻结任何不灵活的项目,这意味着我们已经可以决定某些项目的大小。 如果我们使用flex-grow
这将包括任何具有flex-grow: 0
项目。 这是我们的弹性项目在容器中剩余空间时的场景。 flex-grow
的初始值为 0,因此它们会变得与它们的最大宽度一样大,然后它们不再从它们的主要尺寸增长。
如果我们使用flex-shrink
,那么这将包括任何带有flex-shrink: 0
项目。 如果我们将flex-shrink
因子设置为 0,我们可以看到在这一步中会发生什么。这些项目在它们的max-content
状态下冻结,因此不会弯曲并安排自己以适应容器。
请参阅 Rachel Andrew (@rachelandrew) 在 CodePen 上的 Pen Smashing Flexbox Series 3: flex: 0 0 auto。
在我们的例子中——使用弹性项目的初始值——我们的项目可以缩小。 所以这些步骤继续进行,算法进入一个循环,在这个循环中计算出分配或带走多少空间。 在我们的例子中,我们使用flex-shrink
因为我们的项目的总大小比容器大,所以我们需要占用空间。
flex-shrink
因子乘以项目内部的基本大小,在我们的例子中是max-content
大小。 这给出了一个减少空间的值。 如果项目仅根据flex-shrink
因子移除空间,那么小项目可能基本上消失,已移除所有空间,而较大的项目仍有空间收缩。
在这个循环中还有一个额外的步骤来检查会变得小于或大于其目标主尺寸的项目,在这种情况下,项目停止增长或缩小。 同样,这是为了避免某些物品与其他物品相比变得很小或很大。
所有这一切都在规范方面进行了简化,因为我没有看过一些更边缘的场景,你通常可以在你的脑海中更进一步,假设你很高兴让 Flexbox 做它的事情并且不是在像素之后完美。 在大多数情况下,记住以下两个事实会起作用。
如果您是从auto
增长的,那么flex-basis
将被视为项目上的任何宽度或高度或max-content
大小。 然后将根据flex-grow
因子使用该大小作为起点来分配空间。
如果您从auto
缩小,则flex-basis
将被视为项目上的任何宽度或高度或max-content
大小。 然后将根据flex-basis
大小乘以flex-shrink
因子来移除空间,因此与项目的最大内容大小成比例地移除空间。
控制增长和收缩
这篇文章的大部分时间我都在描述 Flexbox 在留给自己的设备时的作用。 当然,您可以通过使用flex
属性来更好地控制您的 flex 项目。 通过了解幕后发生的事情,他们有望看起来更可预测。
通过设置你自己的flex-basis
,或者给项目本身一个尺寸,然后将其用作flex-basis
你从算法中收回控制权,告诉 Flexbox 你想从这个特定的尺寸增长或缩小。 您可以通过将flex-grow
或flex-shrink
设置为 0 来完全关闭增长或收缩。然而,在这一点上,值得使用控制弹性项目的愿望来检查您是否使用了正确的布局方法。 如果您发现自己试图在二维中排列弹性项目,那么您可能会更好地选择网格布局。
调试大小相关问题
如果您的 flex 项目最终达到了意外的大小,那么这通常是因为您的 flex-basis 是自动的,并且有一些东西给了该项目一个宽度,然后将其用作 flex-basis。 在 DevTools 中检查项目可能有助于确定大小的来源。 您还可以尝试将flex-basis
设置为0
,这将强制 Flexbox 将项目视为具有零宽度。 即使这不是您想要的结果,它也将有助于确定使用中的flex-basis
值是造成您的尺寸问题的罪魁祸首。
弹性间隙
Flexbox 的一个非常需要的特性是能够指定弹性项目之间的间隙或间距,就像我们可以在网格布局和多列布局中指定间隙一样。 这个特性是为 Flexbox 指定的,作为 Box Alignment 的一部分,并且第一个浏览器实现正在进行中。 Firefox 期望在 Firefox 63 中为 Flexbox 提供间隙属性。可以在 Firefox Nightly 中查看以下示例。
请参阅 CodePen 上 Rachel Andrew (@rachelandrew) 的 Pen Smashing Flexbox 系列 3:flex-gaps。

与网格布局一样,在将空间分配给弹性项目之前,会考虑间隙的长度。
包起来
在这篇文章中,我试图解释一些关于 Flexbox 如何计算 flex 项有多大的细节。 这似乎有点学术性,但是,花一些时间来理解它的工作方式可以为您在布局中使用 Flexbox 节省大量时间。 我发现回到这样一个事实真的很有帮助,默认情况下,Flexbox 试图为您提供一堆不同大小的项目的最合理的布局。 如果一个项目有更多的内容,它会被赋予更多的空间。 如果你和你的设计不同意 Flexbox 认为最好的,那么你可以通过设置自己的flex-basis
来收回控制权。