flex 布局的浏览器兼容性方案

7,645 阅读11分钟

前言

flex 布局在目前前端开发中使用到的概率还是蛮大的,尽管它从诞生到现在已经经历了 N 年了,但是从个人的工作圈子来看,有很多人其实对 flex 布局的理解还是很表面,比如一味地使用 flex: 1 或者 flex: auto 等,导致一旦出现一些样式问题或者浏览器兼容问题,马上就无法处理了。

因此,个人从 flex 布局基础知识和浏览器兼容性两个方面,结合个人工作经验,整理了一篇文章,希望对大家有帮助,如有阐述有误之处,还望不吝指教!

文章大体分为两个部分,第一块是 flex 的基础知识,包括布局特性、属性特征等,相对偏理论些。第二块是 flex 的浏览器兼容问题总结,以及 flex 使用建议,这源于个人工作场景中实际碰到的问题,经尝试解决后的总结,偏实际应用一点。

如果对 flex 布局原理比较了解,但遇到浏览器兼容问题的同学来说,可以直接看第二块内容。

flex 布局 - 一维布局

flexbox 是一种一维的布局,因为一个 flexbox 一次只能处理一个维度上的元素布局,一行或者一列

flex 布局 - 容器属性

display: flex vs display: inline-flex

相同点

  • 对于容器内部的子元素而言,没有任何区别

差异

  • 对于容器本身而言,flex 表现为 块级元素;inline-flex 表现为 块级内联元素
    • 因此未给容器设置宽度时,flex 容器默认宽度为100%(一行占满);inline-flex 默认宽度会根据子元素的宽度去自适应

图片.png

flex-direction

轴线 - 主轴 & 交叉轴

  • 主轴由 flex-direction 定义,可取 row(横向主轴)、column(纵向主轴)等值。
  • 交叉轴则为与主轴垂直的轴线。
  • flexbox 的特性是沿着主轴或者交叉轴对齐之中的元素。

图片.png
图片.png

起始线 & 终止线

起始线、终止线用于描述 flexbox 子元素的书写(排列)方向。

当主轴为 row(横向主轴)时:

  • 书写英文时,主轴起始线为左边;书写阿拉伯文时,主轴起始线为右边。
  • 交叉轴起始线是 flexbox 的顶部,终止线是底部,因为两种语言都是水平书写模式。

图片.png
图片.png
当主轴为 column(纵向主轴)时:

  • 主轴起始线为 flexbox 的顶部,终止线为 flexbox 的底部,因为两种语言都是水平书写模式。
  • 书写英文时,交叉轴起始线为左边;书写阿拉伯文时,交叉轴起始线为右边。

属性值与轴线、起始终止线的关系

假定以 row 为基准,那么取其他属性时的线位变化如下:

  • row-reverse:主轴 & 交叉轴不变;起始线 & 终止线 改变
    • 主轴的起始线、终止线互换;交叉轴的起始线、终止线不变
  • column:主轴 & 交叉轴互换;起始线 & 终止线 改变
    • 主轴的起始终止线,与交叉轴的起始终止线互换
  • column-reverse:主轴 & 交叉轴互换;起始线 & 终止线 改变
    • 主轴的起始终止线,与交叉轴的起始终止线互换
    • 交叉轴的起始线、终止线互换;主轴的起始线、终止线不变

justify-content

使子元素在主轴上对齐

  • 初始值是 flex-start,子元素从容器的起始线开始排列。
  • flex-end,元素从容器终止线开始排列
  • center,居中排列,每个元素紧邻
  • space-between,占满容器,并且元素之间间隔相等
  • space-around,占满容器,并且每个元素的左右空间相等

图片.png

align-items

使子元素在交叉轴上对齐

  • 初始值是 stretch
    • 未定义容器高度,容器会被拉伸到最高元素的高度;
    • 定义了容器高度,元素会被拉伸(收缩)到容器高度。
  • flex-start,按容器的起始线对齐
  • flex-end,按容器的终止线对齐
  • center,居中对齐

图片.png

align-content

使子元素在交叉轴方向对齐,但为多行对齐

  • 默认值是 stretch,剩余空间被所有行平分,以扩大它们的交叉轴尺寸
  • flex-start,所有行从容器的起始线排列
  • flex-end,所有行从容器终止线开始排列
  • center,所有行在容器中间,紧凑排列
  • space-between,所有行占满容器,并且每行之间间隔相等
  • space-around,所有行占满容器,并且每行的上下空间相等

图片.png

flex-wrap

换行方式

  • 默认 nowrap,不换行
  • wrap,换行
  • wrap-reverse,换行反向排列

图片.png

flex-flow

flex-directionflex-wrap 的合并写法

flex 布局 - 子元素属性

子元素默认样式

  • flex-direction: row,元素排列为一行,并从主轴的起始线开始
  • flex: 0 1 auto,元素不会在主轴方向拉伸,但会随可用空间会缩小
  • flex-wrap: nowrap,如果有太多元素超出容器,它们会溢出而不会换行
  • 如果一些元素比其他元素高,那么元素会沿交叉轴被拉伸来填满它的大小

图片.png

flex-basis

元素的初始(基准)空间大小

  • 默认值是 auto
    • 元素设置了宽度,flex-basis 为设置的宽度
    • 元素未设置宽度,flex-basis 为元素内容的尺寸
  • flex-basis 属性优先于 width 属性;
  • 设为 0 ,则子元素的大小不在空间分配计算的考虑之内

flex-grow

元素沿主轴方向的扩张尺寸,会占据主轴上的可用空间

  • 按比例分配空间(默认为 0 ),扩张值为 flex-basis 基准乘以 flex-grow 扩张比例
    • 设置一样的值,则平分可用空间
    • 设置不同的值,按比例平分可用空间

下图为不同 flex-basis 的情况下,flex-grow 均设为 1 的场景:
图片.png

flex-shrink

元素沿主轴方向的收缩尺寸,只有在子元素总和超出主轴才会生效

  • 按比例分配空间(默认为 1 ),收缩值为 flex-basis 基准乘以 flex-shrink 收缩比例
  • 随着盒子越来越小,小的子元素最终会以 min-content 的大小进行铺设,后续空间会一直从大的子元素中移除
    • 所谓 min-content ,即当前容器内部最小的不可断行元素的宽度
      • 下图 largeSizeContentWithoutWrap 是连续不可断行的,所以无法再压缩空间
      • 下图 middle-size-content-width-split-code 可以以横杠 - 断行,所以可以换行以压缩空间
      • 下图 content-content-content-content-content-content 已经被断行到不可再断行的宽度,因此无法再继续换行来压缩空间

下图为不同 flex-basis 的情况下,flex-shrink 均设为 1 的场景:
图片.png

order

项目的排列顺序

  • 数值越小,排列越靠前,默认为 0

align-self

允许单个项目有与其他项目不一样的对齐方式

  • 可覆盖 align-items 属性,默认值为 auto
  • 表示继承父元素的 align-items 属性
    • 如果没有父元素,则等同于stretch

几个 flex 样式的简写

flex: initial

相当于 flex: 0 1 auto

flex: auto

相当于 flex: 1 1 auto

flex: none

相当于 flex: 0 0 auto

flex: 1

相当于 flex: 1 1 0

flex: auto 与 flex: 1 的区别

flex: 1 1 auto

  • 在各元素初始宽度基础上,平均分配可用空间
  • 各元素宽度不相等,初始宽度大的分配后宽度也大

flex: 1 1 0

  • 在各元素 0 宽度的基础上,平均分配可用空间
  • 各元素宽度相等

图片.png

flex 布局 - 浏览器兼容性

浏览器兼容性概览

  • 红色部分为不支持 flex 规范
  • 黄色部分为只支持老的 09 版 flex 规范(display: box; )
  • 绿色部分为完全支持新的 12 版 flex 规范(display: flex; )

图片.png

flex 规范,09 版 vs 12 版

图片.png
图片.png
图片.png

支持少数低版本浏览器的方案

  • 联合使用支持 09 版和 12 版的 flex 布局
.container {
	display: box; 
	display: flex; 
}

注意
  • 浏览器兼容语法(-webkit- 等)可以通过postcss 等工具实现
  • 但 09 版 flex 不支持很多特性

chrome 49 的兼容性问题

问题描述

父元素 flex: 1 ,子元素 height: 100% ,因内容较少无法填充满父元素
图片.png

原因

父元素未设置 height,故子元素获取不到父元素的 height

解决方案一

通过父元素 absolute,子元素 relative,这样子元素的高度就会根据父元素的高度进行计算

解释

规范中有提到,如果包含块的高度没有显式地指定(高度由内容决定),并且不是绝对定位元素,则计算值为 auto,高度和百分比值是没办法进行计算的! auto * 100/100 = NaN

缺陷

子元素 absolute 带来的影响,比如需要再设置 width: 100%

解决方案二

子元素不使用 height: 100%,而使用 flex-grow 来占满空间

解决方案三

父元素不使用 flex: 1,而使用 display: flex; + height: 100%;

解释

应用于 display: flex 的元素,使其成为 flex 容器。
这会自动设置 align-items: stretch,会告诉 child(.item)扩展父级的完整高度。
图片.png

总结

Chrome49 浏览器针对子元素设置 height: 100% 后因内容较少无法填充满父元素的情况,
建议父元素使用 display: flex; + height: 100%;

chrome 79 以上的兼容性问题

问题描述

当父元素设置 flex: 1 填充满容器,子元素设置 height: 100% 后,子元素内容过多会超出父元素
图片.png

解决方案

对于一个设置了 flex: 1 的元素,再对其设置 min-height:0,保证内容不超出外层容器

解释

父元素设置 min-height: 0 相当于告诉子元素父元素 height > 0,子元素可以由此间接地拿到父元素的高度,然后设置 height: 100% 保持父元素同样的高度,避免溢出
图片.png

注意

要兼容 chrome49 的话还需使用 display: flex; + height: 100%; 替换 flex: 1

总结

Chrome79 以上浏览器,针对子元素设置 height: 100% 因内容过多超出父元素的情况,建议父元素使用 min-height: 0;(Chrome 79) + display: flex; + height: 100%; (Chrome 49)

chrome 49 与 chrome 79 以上的另一个差异点

差异描述

  • flexbox 一个子元素设置了 flex,另一个子元素设置了很高的高度
  • 当 flexbox 高度不足以容纳两者时,两者高度都会受到压缩
  • 但在 chrome49 下设置了 flex 的子元素,会被压缩到连其子元素都无法完整展示
  • 而 chrome79 下设置了 flex 的子元素,则会尽可能保留其子元素的展示

chrome 49
图片.png
chrome 79 以上
图片.png

解决方案

若要保持设置了 flex 的子元素的完整展示,最好设置 flex 的同时设置 flex-shrink: 0;

QQ 浏览器 10 下 flex: auto 不生效的兼容问题

问题描述

QQ 浏览器 10.6 chromium 70,父元素 flex: auto,子元素 height: 100% 的情况下,无法撑开或收缩子元素
图片.png

解决方案

需要将 flex: auto 改成 flex: 1 而且要带上 min-height: 0

flex 布局使用建议

针对横向布局

按照一般写法即可

.container {
	display: flex;
	flex-direction: row;
}

针对纵向布局

需要增加兼容 chrome49 以及 chrome79 以上的样式

.container {
	display: flex;
	flex-direction: column;
	height: 100%;
	min-height: 0;
}

针对子元素

视情况灵活使用 flex-basisflex-growflex-shrink 的组合,而不是一味地全部使用 flex:1 或者 flex: auto

最后

附上案例项目的 git 地址:

github.com/hezhikai/bl… (案例可能写的不是很好-0-)

重申几点

  • 上面 flex 布局的基础知识是基于自己看过的很多文档归纳的,可能有些描述摘录了其他地方的文章的用词或片段,如有侵权请告知,我会删掉。
    • 因为整理的时候查的资料比较多,现在很多地方已经想不起来参考了哪里的文章,所以不大好去附加参考资料,请见谅。
  • 后面 flex 布局的兼容性问题,更多的是基于工作场景遇到并解决后整理的。当然也有查各方面资料(虽说目前网上这块的资料不够详尽),但是也有个人经验的总结在里面。
    • 因此后面如果个人再遇到其他 flex 的浏览器兼容性问题,还会再总结补充进来。
  • 虽说这篇文章不能算是完全原创,但是毕竟也是个人花了时间和精力整理、基于实际工作场景尝试和总结的,因此如需转载,还请附上此出处哈!
    • 也算是对个人总结的一些认可吧 -.-