Tailwind: 使用 flex 和 grid 进行响应式布局

21751
2022/11/18 19:45:17

本章我们将讨论使用 flexgrid 进行响应式布局,以及为什么不建议再使用 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 提供了专门用于布局的 flexgrid

flex 更适合一维布局,而 grid 更适合多维布局。当然这不是绝对的,通过嵌套 flex 也可以实现多维布局;同样地,grid 都能用来多维布局,一维布局更不在话下。我是想告诉读者,要学会在不同的场景选择最适合的技术。

Tailwind 官方文档中,专门将 flexgrid 放在同一个专题里进行描述,让我们一起来体验新时代的布局。

为了便于课程讲解,从本章开始的示例代码中,我会给每个元素加上 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 值实现

响应式布局

我们现在想实现这么一个效果:

  • 在移动设备上
    • #left#right 是垂直排列的,并且占满整个 #main 的宽度
    • 它们之间的间隙是 3
  • 在桌面设备上
    • #left#right 是水平排列的,分别占 1/32/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-rowmd断点,设置水平布局
    • md:space-y-0md断点,设置垂直方向的间隙为0
    • md:space-x-2md断点,设置水平方向的间隙为2个单位
  • #left
    • w-full:设置为占满整个父容器的宽度
    • md:w-1/3md断点,设置宽度为父容器的三分之一
  • #right
    • w-full:设置为占满整个父容器的宽度
    • md:w-2/3md断点,设置宽度为父容器的三分之二

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>

查看效果会发现,gridflex 一样,可以通过 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:设置垂直方向间隙为3gap文档
    • md:grid-cols-3md断点,设置每行3列进行布局
    • md:gap-y-0md 断点,设置垂直方向间隙为0
    • md:gap-x-2md断点,设置水平方向间隙为2
  • #right
    • col-span-1:设置占用1列
    • md:col-span-2md断点,设置占用2列