# Flex 布局

背景知识: flex (opens new window), flex 布局的基本概念 (opens new window)

Flex 布局的全称为 CSS Flexible Box Layout Module,是 W3C 提出的一种新型页面布局方案,第一个版本于 2009 年推出,到现在为止,W3C 一共发布了 12 个版本,最新版本 (opens new window)于 20171019 推出,已经得到了所有主流浏览器的支持,所以请大胆的使用吧~

# 历史版本

https://www.w3.org/TR/2016/CR-css-flexbox-1-20160526/ (opens new window)
https://www.w3.org/TR/2016/CR-css-flexbox-1-20160301/ (opens new window)
https://www.w3.org/TR/2015/WD-css-flexbox-1-20150514/ (opens new window)
https://www.w3.org/TR/2014/WD-css-flexbox-1-20140925/ (opens new window)
https://www.w3.org/TR/2014/WD-css-flexbox-1-20140325/ (opens new window)
https://www.w3.org/TR/2012/CR-css3-flexbox-20120918/ (opens new window)
https://www.w3.org/TR/2012/WD-css3-flexbox-20120612/ (opens new window)
https://www.w3.org/TR/2012/WD-css3-flexbox-20120322/ (opens new window)
https://www.w3.org/TR/2011/WD-css3-flexbox-20111129/ (opens new window)
https://www.w3.org/TR/2011/WD-css3-flexbox-20110322/ (opens new window)
https://www.w3.org/TR/2009/WD-css3-flexbox-20090723/ (opens new window)

# Flexbox 原理演示

来自[xluos]的Flexbox 演示站 (opens new window)~

Flex 布局由容器flex container和项目flex item两部分组成,容器默认存在两根轴:水平的主轴main axis和垂直的交叉轴cross axis,项目默认以主轴排列。 Flex 属性包括容器属性和项目属性两部分,容器上可设置:flex-directionflex-wrapflex-growjustify-contentalign-itemsalign-content6 个属性,项目上同样可设置 6 个属性,分别为:orderflex-growflex-shrinkflex-basisflexalign-self。示例如下:

# 容器属性

# flex-direction 属性

作用: 决定主轴的方向。

flex-direction: row | row-reverse | column | column-reverse;
1
  • row:默认值,主轴为水平方向,表示从左向右排列
  • row-reverse:主轴为水平方向,从右向左排列
  • column:主轴为垂直方向,从上向下排列
  • column-reverse:主轴为垂直方向,从下向上排列
<style>
  .main-flex-demo1 {
    width: 100%;
    padding: 52px 17px 52px 29px;
  }
  .main-flex-demo1 .container {
    display: flex;
    flex-direction: row;
  }
  .main-flex-demo1 .item {
    width: 20%; height: 29px;
    background: #b4a078;
    border-radius: 5px;
    margin: 12px;
    margin-left: 0;
  }
</style>
<template>
  <div class="main-flex-demo1">
    <span class="radio-wrap" v-for="radio in radios">
      <input
        type="radio"
        :id="radio.id"
        :value="radio.value"
        v-model="flexDirection">
      <label :for="radio.id" @click="handleSelected(radio.id)">
        {{ radio.value }}
      </label>
    </span>
    <div class="container" :style="{ flexDirection }">
      <span
        class="item"
        v-for="$ in elements"
        :style="{ opacity: 1 - $ / 10 }">
      </span>
    </div>
  </div>
</template>
<script>
  export default {
    data() {
      return {
        elements: Array.from({ length: 5 }).map((v, i) => i + 1),
        radios: [
          { id: 'row', value: 'row' },
          { id: 'row-reverse', value: 'row-reverse' },
          { id: 'column', value: 'column' },
          { id: 'column-reverse', value: 'column-reverse' },
        ],
        flexDirection: 'row',
      }
    },
    methods: {
      handleSelected(dir) {
        this.flexDirection = dir;
      }
    }
  }
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
显示代码 复制代码

# flex-wrap 属性

作用: 决定项目在一条轴线排不下时如何换行。

flex-wrap: nowrap | wrap | wrap-reverse;
1
  • nowrap:默认值,不换行
  • wrap:换行,第一行在上方
  • row-reverse:换行,第一行在下方
<style>
  .main-flex-demo2 {
    width: 100%;
    padding: 52px 17px 52px 29px;
  }
  .main-flex-demo2 .container {
    display: flex;
    flex-wrap: nowrap;
  }
  .main-flex-demo2 .item {
    width: 20%; height: 29px;
    max-width: 155px;
    background: #b4a078;
    border-radius: 5px;
    margin: 12px;
    margin-left: 0;
  }
</style>
<template>
  <div class="main-flex-demo2">
    <span class="radio-wrap" v-for="radio in radios">
      <input
        type="radio"
        :id="radio.id"
        :value="radio.value"
        v-model="flexWrap">
      <label :for="radio.id" @click="handleSelected(radio.id)">
        {{ radio.value }}
      </label>
    </span>
    <div class="container" :style="{ flexWrap }">
      <span
        class="item"
        v-for="$ in elements"
        :style="{ opacity: 1 - $ / 10 }">
      </span>
    </div>
  </div>
</template>
<script>
  export default {
    data() {
      return {
        elements: Array.from({ length: 6 }).map((v, i) => i + 1),
        radios: [
          { id: 'nowrap', value: 'nowrap' },
          { id: 'wrap', value: 'wrap' },
          { id: 'wrap-reverse', value: 'wrap-reverse' },
        ],
        flexWrap: 'nowrap',
      }
    },
    methods: {
      handleSelected(dir) {
        this.flexWrap = dir;
      }
    }
  }
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
显示代码 复制代码

# flex-glow 属性

作用: flex-direction属性和flex-wrap属性的简写形式,默认值为 row nowrap。

flex-wrap: <flex-direction> || <flex-wrap>;
1
  • row nowrap:默认值,主轴为水平方向,不换行
  • <flex-direction>:同flex-direction
  • <flex-wrap>:同flex-wrap

# justify-content 属性

作用: 定义项目在主轴上的对齐方式。

justify-content: flex-start | flex-end | center | space-between | space-round |
  space-evenly;
1
2
  • flex-start:默认值,左对齐
  • flex-end:右对齐
  • center:居中
  • space-evenly:每个项目之间及两端的间隔都相等
  • space-around:每个项目两侧间隔相等
  • space-between:两端对齐,项目之间间隔相等
<style>
  .main-flex-demo3 {
    width: 100%;
    padding: 52px 17px 52px 29px;
  }
  .main-flex-demo3 .container {
    display: flex;
    justify-content: flex-start;
  }
  .main-flex-demo3 .item {
    width: 20%; height: 29px;
    background: #b4a078;
    border-radius: 5px;
    margin: 12px;
    margin-left: 0;
  }
</style>
<template>
  <div class="main-flex-demo3">
    <span class="radio-wrap" v-for="radio in radios">
      <input
        type="radio"
        :id="'justifyContent' + radio.id"
        :value="radio.value"
        v-model="justifyContent">
      <label :for="'justifyContent' + radio.id" @click="handleSelected(radio.id)">
        {{ radio.value }}
      </label>
    </span>
    <div class="container" :style="{ justifyContent }">
      <span
        class="item"
        v-for="$ in elements"
        :style="{ opacity: 1 - $ / 10 }">
      </span>
    </div>
  </div>
</template>
<script>
  export default {
    data () {
      return {
        elements: Array.from({ length: 3 }).map((v, i) => i + 1),
        radios: [
          { id: 'flex-start', value: 'flex-start' },
          { id: 'flex-end', value: 'flex-end' },
          { id: 'center', value: 'center' },
          { id: 'space-evenly', value: 'space-evenly' },
          { id: 'space-around', value: 'space-around' },
          { id: 'space-between', value: 'space-between' },
        ],
        justifyContent: 'flex-start',
      }
    },
    methods: {
      handleSelected(dir) {
        this.justifyContent = dir;
      }
    }
  }
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
显示代码 复制代码

# align-items 属性

作用: 定义项目在交叉轴(默认方向从上到下)上的对齐方式。

align-items: flex-start | flex-end | center | baseline | stretch;
1
  • flex-start:交叉轴的起点对齐
  • flex-end:交叉轴的终点对齐
  • cente:交叉轴的中心对齐
  • baseline:项目第一行文字的基线对齐
  • stretch:默认值,项目未设置固定高度时,将占满整个容器
<style>
  .main-flex-demo4 {
    width: 100%;
    padding: 52px 17px 52px 29px;
  }
  .main-flex-demo4 .container {
    display: flex;
    align-items: stretch;
  }
  .main-flex-demo4 .item {
    width: 20%; height: 29px;
    background: #b4a078;
    border-radius: 5px;
    margin: 12px;
    margin-left: 0;
  }
</style>
<template>
  <div class="main-flex-demo4">
    <span class="radio-wrap" v-for="radio in radios">
      <input
        type="radio"
        :id="'alignItems' + radio.id"
        :value="radio.value"
        v-model="alignItems">
      <label :for="'alignItems' + radio.id" @click="handleSelected(radio.id)">
        {{ radio.value }}
      </label>
    </span>
    <div class="container" :style="{ alignItems }">
      <span
        class="item"
        v-for="$ in elements"
        :style="{ opacity: 1 - $ / 10, height: 29 *($* .6 + .4) + 'px' }">
      </span>
    </div>
  </div>
</template>
<script>
  export default {
    data () {
      return {
        elements: Array.from({ length: 5 }).map((v, i) => i + 1),
        radios: [
          { id: 'flex-start', value: 'flex-start' },
          { id: 'flex-end', value: 'flex-end' },
          { id: 'center', value: 'center' },
          { id: 'baseline', value: 'baseline' },
          { id: 'stretch', value: 'stretch' },
        ],
        alignItems: 'stretch',
      }
    },
    methods: {
      handleSelected(dir) {
        this.alignItems = dir;
      }
    }
  }
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
显示代码 复制代码

# align-content 属性

作用: 定义了多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用。

align-content: flex-start | flex-end | center | space-between | space-around |
  stretch;
1
2
  • flex-start:交叉轴的起点对齐
  • flex-end:交叉轴的终点对齐
  • center:交叉轴的中心对齐
  • space-between:与交叉轴两端对齐,轴线之间的间隔平均分布等
  • space-around:每根轴线两侧的间隔都相
  • stretch:默认值,轴线占满整个交叉轴
<style>
  .main-flex-demo5 {
    width: 100%;
    padding: 52px 17px 52px 29px;
  }
  .main-flex-demo5 .container {
    height: 399px;
    display: flex;
    flex-wrap: wrap;
    align-content: stretch;
  }
  .main-flex-demo5 .item {
    width: 20%; height: 29px;
    max-width: 155px;
    background: #b4a078;
    border-radius: 5px;
    margin: 12px;
    margin-left: 0;
  }
</style>
<template>
  <div class="main-flex-demo5">
    <span class="radio-wrap" v-for="radio in radios">
      <input
        type="radio"
        :id="'alignContent' + radio.id"
        :value="radio.value"
        v-model="alignContent">
      <label :for="'alignContent' + radio.id" @click="handleSelected(radio.id)">
        {{ radio.value }}
      </label>
    </span>
    <div class="container" :style="{ alignContent }">
      <span
        class="item"
        v-for="$ in elements"
        :style="{ opacity: 1 - ($ * .6) / 10 }">
      </span>
    </div>
  </div>
</template>
<script>
  export default {
    data () {
      return {
        elements: Array.from({ length: 15 }).map((v, i) => i + 1),
        radios: [
          { id: 'flex-start', value: 'flex-start' },
          { id: 'flex-end', value: 'flex-end' },
          { id: 'center', value: 'center' },
          { id: 'space-between', value: 'space-between' },
          { id: 'space-around', value: 'space-around' },
          { id: 'stretch', value: 'stretch' },
        ],
        alignContent: 'stretch',
      }
    },
    methods: {
      handleSelected(dir) {
        this.alignContent = dir;
      }
    }
  }
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
显示代码 复制代码

# order 属性

作用: 定义项目的排列顺序。

order: <number>;
1
  • <number>:值为整数,数值越小,排列越靠前,默认为 0
👉🏿🔀: 0
order: 1 order: 2 order: 3 order: 4 order: 5
<style>
  .main-flex-demo6 {
    width: 100%;
    padding: 52px 17px 52px 29px;
  }
  .main-flex-demo6 a {
    cursor: pointer;
  }
  .main-flex-demo6 .container {
    display: flex;
  }
  .main-flex-demo6 .item {
    width: 20%; height: 29px;
    background: #b4a078;
    border-radius: 5px;
    margin: 12px;
    margin-left: 0;
    color: #f4f0ea;
    text-align: center;
    padding-top: 4px;
  }
</style>
<template>
  <div class="main-flex-demo6">
    <a @click="shuffle">👉🏿🔀: {{ res }}</a>
    <div class="container">
      <span
        class="item"
        v-for="$ in elements"
        :style="{ order: $.order, opacity: 1 - $.id / 10 }">
        order: {{ $.order }}
      </span>
    </div>
  </div>
</template>
<script>
  export default {
    data() {
      return {
        elements: [
          { id: 1, order: 1 },
          { id: 2, order: 2 },
          { id: 3, order: 3 },
          { id: 4, order: 4 },
          { id: 5, order: 5 },
        ],
        res: 0,
      }
    },
    methods: {
      shuffle() {
        const i = Math.floor(Math.random() *5);
this.res = this.elements[i].order = Math.floor(Math.random()* 29);
      }
    }
  }
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
显示代码 复制代码

# flex-grow 属性

作用: 定义项目的伸缩比例,按照该比例给项目分配空间。

flex-grow: <number>;
1
  • <number>:值为整数,数值越大,项目占据空间越大,默认为 0
👉🏿🔀: 1
flexGrow: 1 flexGrow: 1 flexGrow: 1 flexGrow: 1 flexGrow: 1
<style>
  .main-flex-demo7 {
    width: 100%;
    padding: 52px 17px 52px 29px;
  }
  .main-flex-demo7 a {
    cursor: pointer;
  }
  .main-flex-demo7 .container {
    display: flex;
  }
  .main-flex-demo7 .item {
    height: 29px;
    background: #b4a078;
    border-radius: 5px;
    margin: 12px;
    margin-left: 0;
    color: #f4f0ea;
    text-align: center;
    padding-top: 4px;
  }
</style>
<template>
  <div class="main-flex-demo7">
    <a @click="shuffle">👉🏿🔀: {{ res }}</a>
    <div class="container">
      <span
        class="item"
        v-for="$ in elements"
        :style="{ flexGrow: $.flexGrow, opacity: 1 - $.id / 10 }">
        flexGrow: {{ $.flexGrow }}
      </span>
    </div>
  </div>
</template>
<script>
  export default {
    data() {
      return {
        elements: [
          { id: 1, flexGrow: 1 },
          { id: 2, flexGrow: 1 },
          { id: 3, flexGrow: 1 },
          { id: 4, flexGrow: 1 },
          { id: 5, flexGrow: 1 },
        ],
        res: 1,
      }
    },
    methods: {
      shuffle() {
        const i = Math.floor(Math.random() *5);
this.res = this.elements[i].flexGrow = Math.floor(Math.random()* 29);
      }
    }
  }
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
显示代码 复制代码

# flex-shrink 属性

作用: 定义项目的收缩比例,按照该比例给项目分配空间。

flex-shrink: <number>;
1
  • <number>:值为整数,数值越大,项目占据空间越小,默认为 0
👉🏿🔀: 1
flexShrink: 0 flexShrink: 1 flexShrink: 2
<style>
  .main-flex-demo8 {
    width: 100%;
    padding: 52px 17px 52px 29px;
  }
  .main-flex-demo8 a {
    cursor: pointer;
  }
  .main-flex-demo8 .container {
    display: flex;
  }
  .main-flex-demo8 .item {
    width: 50%; height: 29px;
    background: #b4a078;
    border-radius: 5px;
    margin: 12px;
    margin-left: 0;
    color: #f4f0ea;
    text-align: center;
    padding-top: 4px;
  }
</style>
<template>
  <div class="main-flex-demo8">
    <a @click="shuffle">👉🏿🔀: {{ res }}</a>
    <div class="container">
      <span
        class="item"
        v-for="$ in elements"
        :style="{ flexShrink: $.flexShrink, opacity: 1 - $.id / 10 }">
        flexShrink: {{ $.flexShrink }}
      </span>
    </div>
  </div>
</template>
<script>
  export default {
    data () {
      return {
        elements: [
          { id: 1, flexShrink: 0 },
          { id: 2, flexShrink: 1 },
          { id: 3, flexShrink: 2 },
        ],
        res: 1,
      }
    },
    methods: {
      shuffle() {
        const i = Math.floor(Math.random() *3);
this.res = this.elements[i].flexShrink = Math.floor(Math.random()* 4);
      }
    }
  }
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
显示代码 复制代码

# flex-basis 属性

作用: 定义在分配多余空间之前,项目占据的主轴空间。浏览器根据这个属性,计算主轴是否有多余空间。

flex-basis: <length> | auto;
1
  • <length>:默认为 auto,即项目的原始尺寸;也可设置和 width 或 height 属性一样的值(比如 329px),则项目将占据固定空间。
👉🏿🔀: 1
flexBasis: auto flexBasis: auto flexBasis: auto
<style>
  .main-flex-demo9 {
    width: 100%;
    padding: 52px 17px 52px 29px;
  }
  .main-flex-demo9 a {
    cursor: pointer;
  }
  .main-flex-demo9 .container {
    display: flex;
  }
  .main-flex-demo9 .item {
    width: 30%; height: 29px;
    background: #b4a078;
    border-radius: 5px;
    margin: 12px;
    margin-left: 0;
    color: #f4f0ea;
    text-align: center;
    padding-top: 4px;
  }
</style>
<template>
  <div class="main-flex-demo9">
    <a @click="shuffle">👉🏿🔀: {{ res }}</a>
    <div class="container">
      <span
        class="item"
        v-for="$ in elements"
        :style="{ flexBasis: $.flexBasis + 'px', opacity: 1 - $.id / 10 }">
        flexBasis: {{ $.flexBasis }}
      </span>
    </div>
  </div>
</template>
<script>
  export default {
    data () {
      return {
        elements: [
          { id: 1, flexBasis: 'auto' },
          { id: 2, flexBasis: 'auto' },
          { id: 3, flexBasis: 'auto' },
        ],
        res: 1,
      }
    },
    methods: {
      shuffle() {
        const i = Math.floor(Math.random() *3);
this.res = this.elements[i].flexBasis = 129 + Math.floor(Math.random()* 300);
      }
    }
  }
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
显示代码 复制代码

# flex 属性

作用:flex-grow,flex-shrinkflex-basis的简写,后两个属性可选。

flex: none | [ < "flex-grow" > < "flex-shrink" >? || < "flex-basis" > ];
1
  • 0 1 auto:默认值,不伸缩,如果容器空间不足则等比例收缩
  • 1 1 auto:对应关键字auto,如果容器空间多余,则等比例分配多余空间;如果容器空间不足则等比例收缩
  • 0 0 auto:对应关键字none,按项目原始大小分配空间

# align-self 属性

作用: 定义单个项目的对齐方式,可覆盖 align-items 属性。

align-self: auto | flex-start | flex-end | center | baseline | stretch;
1
  • auto:默认值,继承父元素的align-items属性,如果没有父元素,则等同于 stretch
  • flex-start:交叉轴的起点对齐
  • flex-end:交叉轴的终点对齐
  • center:交叉轴的中心对齐
  • baseline:项目第一行文字的基线对齐
  • stretch:未设置固定高度是,将占满整个容器
3 1 5 4 2
<style>
  .main-flex-demo10 {
    width: 100%;
    padding: 52px 17px 52px 29px;
  }
  .main-flex-demo10 .container {
    height: 129px;
    display: flex;
  }
  .main-flex-demo10 .item {
    width: 20%; min-height: 29px;
    text-align: center;
    background: #b4a078;
    border-radius: 5px;
    margin: 12px;
    margin-left: 0;
    color: #f4f0ea;
  }
</style>
<template>
  <div class="main-flex-demo10">
    <span class="radio-wrap" v-for="radio in radios">
      <input
        type="radio"
        :id="'alignSelf' + radio.id"
        :value="radio.value"
        v-model="alignSelf">
      <label :for="'alignSelf' + radio.id" @click="handleSelected(radio.id)">
        {{radio.value}}
      </label>
    </span>
    <div class="container">
      <span
        class="item"
        v-for="$ in elements"
        :style="{
          alignSelf: alignSelf,
          opacity: 1 - $ / 10,
          fontSize: 15 + 12 * $ + 'px'
        }"
        >{{$}}
      </span>
    </div>
  </div>
</template>
<script>
  export default {
    data () {
      return {
        elements: [3, 1, 5, 4, 2],
        radios: [
          { id: 'auto', value: 'auto' },
          { id: 'flex-start', value: 'flex-start' },
          { id: 'flex-end', value: 'flex-end' },
          { id: 'center', value: 'center' },
          { id: 'baseline', value: 'baseline' },
          { id: 'stretch', value: 'stretch' },
        ],
        alignSelf: 'auto',
      }
    },
    methods: {
      handleSelected(val) {
        this.alignSelf = val;
      }
    }
  }
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
显示代码 复制代码
上次更新: 2023-10-28