一道前端面试题:flex空间分配规则

1,901 阅读3分钟

这是字节跳动的的一道面试题。

原题

* {
	padding: 0;
	margin: 0;
}
.container {
	width: 600px;
	height: 300px;
	display: flex;
}
.left {
	width: 500px;
	flex-shrink: 2;
	background: red;
}
.right {
	width: 400px;
	flex-shrink: 1;
	background: blue;
}

求最终left和right的宽度。

解析

先看实际展示的效果

left: 285.72px

right: 314.28px

从上面的结果来看,当子容器宽度之和超出父容器宽度之后不是仅仅按照500:400 或者 2:1来计算的,实际计算过程应该是这个样子的:

  1. 计算出超出部分:500+400-600=300
  2. 注意,重点来了,超出部分实际的分配比例:500x2:400x1 也就是5:2
  3. 那么left的宽就是: 500 - 3005/7 ≈ 285.71, right: 400 - 3002/7 ≈ 314.29, 符合实际情况。

延伸

我们加上padding

* {
	padding: 0;
	margin: 0;
}
.container {
	width: 600px;
	height: 300px;
	display: flex;
}
.left {
	width: 500px;
	padding: 40px;
	flex-shrink: 2;
	background: red;
}
.right {
	width: 400px;
	padding: 20px;
	flex-shrink: 1;
	background: blue;
}

实际效果是:

left: 280px

right: 320px

先想想结果为什么会变化?

原因是

  1. 当我们不设置box-sizing时,其默认值是content-box,也就是标准盒模型,盒子的宽是不包括paddingborder的,所以如果不考虑父容器的宽度,left真正占据的空间应该是500 + 40x2 = 580, 而right则是:400 + 40x2=440。
  2. flex项计算可用空间时,padding部分会被冻结不参与分配。于是left的可用空间就是 580-40x2=500, right的可用空间是440-20x2=400。

下面是w3c对flex布局中可用空间的描述

w3c: Determine the available main and cross space for the flex items. For each dimension, if that dimension of the flex container’s content box is a definite size, use that; if that dimension of the flex container is being sized under a min or max-content constraint, the available space in that dimension is that constraint; otherwise, subtract the flex container’s margin, border, and padding from the space available to the flex container in that dimension and use that value. This might result in an infinite value.

这里提到flex项的可用空间要减去margin、border、padding。

所以这里计算的过程就是:

  1. 计算超出部分,按照真正占用空间计算:580+440-600=420
  2. 超出部分实际的分配比例,不包含padding:500x2:400x1 也就是5:2
  3. 那么left的宽就是: 580 - 420x5/7 = 280, right的宽是: 440 - 420x2/7 = 320, 符合实际情况。

ok,那我们再看看 box-sizing: border-box的情况

* {
		padding: 0;
		margin: 0;
	}
	.container {
		width: 600px;
		height: 300px;
		display: flex;
	}
	.left {
		width: 500px;
		padding: 40px;
		flex-shrink: 2;
		background: red;
		box-sizing: border-box;
	}
	.right {
		width: 400px;
		padding: 20px;
		flex-shrink: 1;
		background: blue;
		box-sizing: border-box;
	}

实际效果是

left: 290px

right: 310px

当我们设置box-sizing: border-box,也就是IE盒模型,盒子的宽是包括paddingborder的,如果不考虑父容器宽度的话,left真正占据的空间依然是500 right是400,padding依然不能参与分配,那么left、right的可用空间分别就变成了500-40x2=420, 400-20x2=360, 所以这里计算的过程就是:

  1. 计算出超出部分, 按真正占用空间计算:500+400-600=300
  2. 超出部分实际的分配比例, 不包含padding:420x2:360x1 也就是7:3
  3. 那么left的宽就是: 500 - 300x7/10 = 290, right的宽是: 400 - 300x3/10 = 310, 符合实际情况。

总结

我们总结一下计算过程

  1. 按照子元素实际占用空间(不考虑父容器宽度的情况下),求出超出部分
  2. 根据子元素实际占用空间减去margin、padding、border后得到的可用空间和flex缩小或放大倍数加权计算超出部分的分配比例
  3. 根据缩减分配比例求出具体缩减像素