HTML/CSS [前端知识] Flexbox,更优雅的布局

gitcafe · January 15, 2015 · Last by tomatoo replied at June 23, 2016 · 12023 hits

上个周末,在北航新主楼会议厅里举行了首届 CSS 开发者大会,GitCafe 的前端工程师 Jaych Su 在会上做了演讲,与大家分享了一款更优雅的前端布局——Flexbox。现在,我们就邀请他来为大家讲一下 Flexbox 的相关知识吧~

在设计的眼中,排版的操作是一件很简单的事情,靠左、置中、靠右,我只要点一下,所有元素,就会乖乖的到指定的位置。

但到了前端在排版的实现上,就不是这样了。

我们常常得用一堆其实本来不是这样用的属性来做 hack,比如说用 line-height 来做垂直置中,这样做的确能达到效果,但是在语意上就有点不顺,拿刚刚提到的 line-height 来说,这本来是用来当作段落中的行距,但却因为这个属性能扩展文字的上下空间,结果也被拿来做垂直置中。那有没有一个方法能用来更好地实现 Web 布局呢?

这是 Google 的 Angular。他们几个月前做了一套 UI 来实现在 Angular 上的 Material Design。这套框架用来实现布局的方式,不是以往的 float,而是用了 Flexbox。

Flexbox 是什么呢?就 W3C 官方给到的解释是,这是设计来实现更复杂的版面布局。那我自己对他的定义是,Flexbox 从本质上就是一个 Box-model 的延伸,我们都知道 Box-model 定义了一个元素的盒模型,然而 Flexbox 更进一步的去规范了这些盒模型之间彼此的相对关系。而不需要去用一些很 cheat 的做法,去 hack 一些本来其实不应该用来做版面布局的属性。

身为一个喜欢去玩一些新东西的前端,应该说每个跟互联网有所接触的人,都需要去学新东西。

这是我碰到新东西的时候,一定会问自己的三个问题:

  1. 这能做什么?也就是他能解决什么问题?
  2. 能用在哪裡?在哪些地方能用这个方法?
  3. 为什么能用?他实现所用到的逻辑是什么?

接下来就跟大家分享一下,当初看到 Flexbox 的我问了自己这个三个问题之后,到目前为止我找到的答案。

功能

举一个例子,所有前端都会有的痛点,置中,我们以前是怎么实现的?

最常看到就是用绝对定位,然后把 top 和 left 偏移 五零%,在用 margin 偏移回去。但是这只适用在已经固定大小的元素。

最近几年常看到的做法是这样,在想置中的元素之前,加上一个元素,不想管太旧的 IE 的话,甚至伪元素也可以。在容器用 text-align,然后把底下的两个元素弄成 inline 的形式,在用 vertical-align。他的好处就是,即使底下的元素会随内容改变大小,但不管怎么改变,就是可以始终维持垂直和水平置中。

当然啦,还有很多置中的方法,就不一一介绍了,我们来看一下用 flex 的话怎么置中。

用 Flex 来做置中的话,你可以很从容地做到置中,不用一堆即使本来不是这样用的属性。我只要先指定容器为一个 Flex 容器,然后 justify-content 让他水平方向置中,再 align-items 让他垂直方向置中。我可以很简单很优雅的就做到置中。

那也许你会说,欸?既然一个可以的话,那我再多放几个可不可以?其实可以的。

假设我们现在容器底下有三个元素,喔,这裡就要提到 Flexbox 另外一个屌炸天的功能。

假设一个元素是四零%,另一个是一二%,那在一个 Flex 容器中,只要你有设定 flex-grow 这项属性的话,他的第三个元素就会自适应宽度,填满剩下容器的空白。而在多个元素的状态之下,我们仍然能很轻易的就置中。

刚刚我们提到过,flexbox 是用来规范盒模型之间的相对关系,从这裡你就可以看到。现在我将 justify-content 设成 space-around,元素就会变成已分散对齐的方式去分佈在 flex 容器中。

关于元素的分步,我们再来看几个例子。

这是一个我最近看到的网站。我们可以看到他底下有一个 Slider,这有个问题,而且也常常是前端在版面上的一个痛点,我们想让所有的子元素能够等高。在以前我们很难只用 CSS 去做到这样。

而 flexbox 可以很轻易地只用 CSS 做到这点。只需要在 flex 容器加上 align-items 就好。就能实现容器底下的所有元素,与最高的那个元素等高。

即使我在本来最高的那个元素多加一些内容,其他的元素也一定会维持等高。

兼容

Flex 最初被 W3C 于 09 年制定出来,随后就被大量的讨论。拿指定元素为一个 flex 容器来讲,第一个版本裡是 display:box,第二个版本是 display: flexbox,第三个版本是 display: flex。实在太复杂,还好现在在开源的世界里已经有大大把这三个版本的 flex 做成一些 mixin,使用的时候,你只要 include 进来就可以。

就跟 IE 的使用体验一样,所有的好东西跟 IE 基本都沾不上边,所以如果你需要考虑 IE 用户,那请慎入。所以有人说 IE 的功能只剩下用来下载 Chrome 和 Firefox。

原理

如果你到网上搜 flex,大多都会著墨在 他的对齐、他的控制 DOM 顺序是如何如何好用。但今天我们想聊一聊更深一点的东西,flex item 宽度的计算,大多数情况下,我们只在意显示的比例,这也是宽度的计算比较少被讨论的原因,但如果你想要更精确的控制 item 的显示宽度,其实你是需要去了解,在一个 flex 容器当中,item 的宽度是如何被计算出来的。

当我们把一个容器指定为 flex 容器时,它裡面的 item 其实是有著这样的设定:flex: 0 1 auto

这三个数字其实分别代表:flex-grow、flex-shrink、flex-basis,这三个属性可以说是 flex 之所以智能的原因。

我们先来聊聊 flex-basis 好了,这个属性在 flex 容器为横向的时候,其实就是宽度,当我们把 item 指定成 flex: 0 0 480px 时,其实就是把它的宽度设定成 480px。但是这样并不能表现出 flex 有什么特别的地方啊?为什麽要重複设定宽度?

这时候就要讲到另外两个属性:flex-grow、flex-shrink

这两个属性其实是双胞胎,grow 表示在 item 总宽度比容器小的时候,为了让 item 填满容器,每个 item 增加的宽度。假设有三个 basis 为 100px 的 item。我们从左到右给予 grow 值分别为 3、2、1,那么当 flex 作用之后,最左边的 item 实际增加的宽度是多少?从图中可以算到增加的宽度是 90px,于是最后最左边 item 的宽度是 190px。

我们刚才提到 grow 跟 shrink 其实是双胞胎,其实他们真的很像,shrink 表示在 item 总宽度比容器大的时候,为了让 item 填满容器,每个 item 减少的宽度。但是计算的公式却是不一样的。为什么?因为当你在加的时候无所谓,但是在减的时候,如果只计算赋予的 shrink 值,那么很有可能最后减少的宽度比 basis 大,于是 item 的宽度就变成负值。那我们该怎么修正?把 basis 当成参数计算进去,这样就能保证减少的宽度永远小于 basis。所以我们可以得到修正后的公式,一样以最左边为例子,最后计算出来减少 60px,于是 item 就变成 140px。以上脑子不好使,没关系,实际上最常用的只是 flex: 1。

讲到这里,你刚刚讲的好像这东西很厉害的样子,那你有没有一个最快最简单粗暴的方式去说 Flexbox 真的是个好东西?

嗯⋯⋯有点难,不过我想应该可以。

半年前用过一下发现 flex-grow 在 safari 下有问题,于是换回基于浮动和百分比的布局。

。。。不错。。。以前试过。。。不过正是规范混乱的时候。。。现在似乎都到 3 了。。。应该是能用了

flexbox 在移动设备上就不用考虑兼容性了,浏览器基本都支持。相比其他方法又能减少 dom 数量,实在是布局利器。

#2 楼 @Rei 等今后趋于更加成熟的版本后 期待你的回归哈!

很赞的文章,自己不动手做做感觉会辜负楼主似的

赞!!! 不过vertical-align: middle;这个方法描述不完整。 还需要对插入的元素也设置vertical-align: middle;

一直非常讨厌 hack 居中的办法,但是也没碰到特别好的解决方案

#5 楼 @darkbaby123 直接用 bootstrap3 这种框架是否更合适?

mobile 开发用 Bootstrap 3 不大合适,主要是 UI 风格感觉有点不搭调。我知道的 mobile UI framework 里只有 Foundation for Apps 和 Ionic 是用 flexbox 做 grid 的。这也间接说明了 flexbox 在 mobile 上的可用性。

请问原理部分 container_blank 计算时的 300 是哪里设置的呢?以及计算 grow 时的 3 2 1 是哪里设置的?

#14 楼 @kyon0304 哈,果然属于脑子笨的人,300 是 basis 100 3 块加起来的值,3 2 1 是 举例的 grow 的值。

移动端还好,PC 端有坑。

You need to Sign in before reply, if you don't have an account, please Sign up first.