本章我们将讨论使用 flex
和 grid
进行响应式布局,以及为什么不建议再使用 float
进行布局。
有关 float
布局
之所以一上来就“批判” float
,是因为用它来进行页面布局的历史太悠久了——我们要批判的不是 float
,而是到今天还在用 float
布局的人。
float
的本意是实现图文混排,就像报刊杂志那样,能让文字环绕在图片周围。在以前的 CSS 2 时代,不知道哪个小聪明(或许是谷某歌)使用float
再配合一些“黑魔法”,让 div
布局替换掉了更可恶的 table
布局。不得不说,这是具有划时代意义的,它让 HTML 语义化迈向了新篇章。
由于 float
本就不是为了布局而生,所以用它来布局需要做很多工作,而且还需要“黑魔法”来清除浮动。让我们来回(xi)顾(shu)一下它的弊端:
- 需要用“黑魔法”来清除浮动——比如鼎(chou)鼎(ming)有(zhao)名(zhu)的
clearfix
- 如果需要实现布局元素的对齐,又要使用“黑魔法”
- 需要手动精确计算参与布局的元素的尺寸
- 如果需要让参与布局的元素之间颜色间隙,又需要手动精确计算
- 如果要实现响应式布局,几何量级的噩梦
到了现在的 CSS 3 时代,让我们跟 float
布局说声再见,让 float
回归它的本职:图文混排。能这么有底气的说这句话,是因为 CSS 3 提供了专门用于布局的 flex
和 grid
。
flex
更适合一维布局,而 grid
更适合多维布局。当然这不是绝对的,通过嵌套 flex
也可以实现多维布局;同样地,grid
都能用来多维布局,一维布局更不在话下。我是想告诉读者,要学会在不同的场景选择最适合的技术。
Tailwind 官方文档中,专门将 flex
和 grid
放在同一个专题里进行描述,让我们一起来体验新时代的布局。
为了便于课程讲解,从本章开始的示例代码中,我会给每个元素加上
id
。它只是为了课程讲解,没有特别的作用。
flex
布局
国人更喜欢将其称为弹性布局,确实挺符合它的气质的。在 tailwind 中,使用 flex
类开启 flex
布局:
<div class="flex"></div>
开始布局
<div class="flex" id="main">
<div class="bg-red-50" id="left">左</div>
<div class="bg-blue-50" id="right">右</div>
</div>
点击这里打开代码并预览效果。你会发现,只需要在父元素(#main
)中加上 flex
类,子元素自动进行了水平方向的布局,子元素根本不需要设置跟布局相关的样式。
#main
flex
:开启flex
布局
#left
bg-red-50
:为了便于查看效果,给它设置50号红色的背景
#right
bg-blue-50
:给它设置50号蓝色背景
接下来,让我们继续优化这个布局
让布局元素更具意义
<div class="flex max-w-2xl mx-auto" id="main">
<div class="bg-red-50 w-1/3 h-52" id="left">左</div>
<div class="bg-blue-50 w-2/3 h-36" id="right">右</div>
</div>
点击这里打开代码并预览效果。你看到,两个子元素已经拥有了宽度和高度,它们水平排列在容器上。同时,虽然两个子元素的高度不一样,但它们在垂直方向上依然保持顶端对齐。
#main
max-w-2xl
:设置整个布局容器的最大宽度是2xl
mx-auto
:实现水平居中
#left
w-1/3
:设置它的宽度是父容器的三分之一- 很神奇的是,tailwind 的类里竟然能写分数形式。这不奇怪,它还提供了一些小数形式的类
- 此例中,我们想精确控制子元素的宽度,所以这里使用了
width
,但它和我们响应式布局的原则并不冲突:- 它是基于它的父容器的宽度设置的,而它的父容器的宽度是通过响应式的
max-w-2xl
指定 - 它使用的是三分之一这种相对值,而不是类似
33px
这样的绝对值
- 它是基于它的父容器的宽度设置的,而它的父容器的宽度是通过响应式的
- 由于我们的内容很少,在没有其它属性的帮助下,
flex
自动计算的宽度会非常小(比如上一步的例子)。我们可以通过很多方式来让这个子元素不那么窄,比如本例通过宽度来实现。
h-52
:设置它的高度是52
#right
w-2/3
:设置它的宽度是父容器的三分之二h-36
:设置它的高度是36
对齐与间距
继续我们的示例,现在,我们想实现这样的效果:
- 两个子元素是从左往右分布的
- 两个子元素之间要有
2
个单位的间隙 - 两个子元素之间,垂直方向不再是顶端对齐,而是底部对齐
#main
justify-start
:设置子元素从左往右(准确说是从头到尾,从开始到结束。对于水平方向来说是从左到右,对于垂直方向来说是从上到下)进行分布,这是默认值。它还有其它值可供设置,比如常用的:justify-end
:从结束到开始进行分布(水平:从右到左;垂直:从下到上)justify-between
:两端分布- 更多可选值请查看官方文档
items-end
:设置子元素的垂直底部对齐,它还有其它值可供设置:items-center
:垂直居中对齐items-start
:默认值。垂直顶部对齐- 更多可选值请查看官方文档
space-x-2
:设置子元素之间的水平间隙为2
。其中的x
代表水平方向,可以改成y
来表示垂直方向,比如space-y-2
。你可以点击这里查看官方文档- 原生 CSS 提供了
gap
,它也可以设置子元素的间隙,我们将在下文的grid
布局里使用到 - 通过查看生成的 CSS 可知,
space
是 tailwind 封装的一个工具类,并不像gap
那样,是原生CSS直接提供的。 space
是通过一系列计算,通过伪类给子元素设置margin
值实现
- 原生 CSS 提供了
响应式布局
我们现在想实现这么一个效果:
- 在移动设备上
#left
和#right
是垂直排列的,并且占满整个#main
的宽度- 它们之间的间隙是
3
- 在桌面设备上
#left
和#right
是水平排列的,分别占1/3
和2/3
的宽度- 它们之间的间隙是
2
首先,我们犯了一个重大错误,违背了「移动设备优先」原则——到目前为止,所有的样式都是为桌面设备设计的。这是因为我们是要讲述基础知识,情有可原。
接下来,我们将对已有代码进行改造,使其不但遵循移动设备优先原则,还能实现需要的响应式效果。
<div class="flex max-w-2xl mx-auto flex-col space-y-3 justify-start items-end md:flex-row md:space-y-0 md:space-x-2" id="main">
<div class="bg-red-50 w-full h-52 md:w-1/3" id="left">左</div>
<div class="bg-blue-50 w-full h-36 md:w-2/3" id="right">右</div>
</div>
你应该像我这样,通过加上
md:
断点来使用移动设备优先的原则实现响应式布局;而不应该通过sm:
断点来使用桌面设备优先来实现响应式布局——虽然在本案例中,通过sm:
会更少工作量
点击这里查看效果,你会发现随时你改变预览窗口的大小,整个布局会发生改变:在小屏幕上,两个子元素是垂直排列的;而在其它屏幕上,它们是水平排列的。
#main
flex-col
:设置按“列”进行布局,就是说设置为垂直布局。- 为什么之前的示例中没有
flex-col
,它会水平布局?因为 flex 默认就是水平布局,你也可以使用flex-row
显式的进行设置 - 你可以点击这里查看 flex 布局方向。
- 为什么之前的示例中没有
space-y-3
:垂直方向设置3
个单位的间隙md:flex-row
:md
断点,设置水平布局md:space-y-0
:md
断点,设置垂直方向的间隙为0md:space-x-2
:md
断点,设置水平方向的间隙为2
个单位
#left
w-full
:设置为占满整个父容器的宽度md:w-1/3
:md
断点,设置宽度为父容器的三分之一
#right
w-full
:设置为占满整个父容器的宽度md:w-2/3
:md
断点,设置宽度为父容器的三分之二
grid
布局
在 tailwind 中,使用 grid
类开启 grid
布局:
<div class="grid"></div>
我们使用 grid
改写上例中的 flex
布局。
开始布局
<div class="grid" id="main">
<div class="bg-red-50" id="left">左</div>
<div class="bg-blue-50" id="right">右</div>
</div>
通过这里,你会看到:和 flex
不同,grid
默认是垂直布局。——真正原因是,grid
默认使用每行1列来布局,而我们这里有两个子元素,它们就会分成两行。
#main
:grid
:开启grid
布局。
通过设置列数来实现水平布局
我们可以通过设置 grid
的列数来实现水平布局
<div class="grid grid-cols-2" id="main">
<div class="bg-red-50" id="left">左</div>
<div class="bg-blue-50" id="right">右</div>
</div>
点击这里你可以看到,两个子元素被均分地布局在水平方向上——我们并没有给子元素设置宽度。
#main
grid-cols-2
- 设置
grid
布局时,每行使用2
列来布局 - 在默认配置中,tailwind 提供了最多
12
列,即grid-cols-12
- 你可以点击这里查看官方文档
- 设置
通过合并来限定子元素宽度
我们知道,设置了列数之后,grid
会均分地布局每个子元素——就是说,每个子元素的宽度是一样的。我们可以配置列数和合并列来实现限定子元素宽度
#main
grid-cols-3
:设置每行使用3列来布局
#right
col-span-2
:- 设置占用
2
列,即合并。 - 在没有设置这个之前,每个子元素占用
1
列,比如#left
元素,它并没有设置,但默认值就是col-span-1
- 官方文档
- 设置占用
grid
不但能设置列、合并列;还能设置行、合并行。这就是grid
多维布局的表现,两者结合可以实现很灵活的布局。
设置对齐
<div class="grid grid-cols-3 items-end" id="main">
<div class="bg-red-50 h-56" id="left">左</div>
<div class="bg-blue-50 col-span-2 h-32" id="right">右</div>
</div>
查看效果会发现,grid
和 flex
一样,可以通过 items-*
来设置垂直对齐方式。
响应式布局
我们来实现和 flex 布局中,最终的响应式布局效果。
<div class="grid grid-cols-1 items-end gap-y-3 md:grid-cols-3 md:gap-y-0 md:gap-x-2" id="main">
<div class="bg-red-50 h-56" id="left">左</div>
<div class="bg-blue-50 col-span-1 h-32 md:col-span-2" id="right">右</div>
</div>
点击这里查看效果。
#main
grid-cols-1
:设置每行1列进行布局gap-y-3
:设置垂直方向间隙为3
【gap
的文档】md:grid-cols-3
:md
断点,设置每行3列进行布局md:gap-y-0
:md
断点,设置垂直方向间隙为0md:gap-x-2
:md
断点,设置水平方向间隙为2
#right
col-span-1
:设置占用1列md:col-span-2
:md
断点,设置占用2列