深入理解 Grid 布局
Grid 网格布局将页面划分成一个个网格,可以任意组合不同的网格,做出各种布局。与 Flex 的区别在于,Flex 布局是轴线布局,只能指定项目针对轴线的位置,可看作一维布局。而 Grid 布局则是将容器划分成行和列,产生单元格,然后指定项目所在的单元格,可看作二维布局,比 Flex 布局强大。
一、容器包裹层属性
1、grid-template-[columns/rows]
- grid-template-columns:设置每一列的列宽;
- grid-template-rows:设置每一行的行高。
<div class="container">
  <div class="item">1</div>
  <div class="item">2</div>
  <div class="item">3</div>
  <!-- ... -->
  <div class="item">9</div>
</div>
.container {
  display: grid;
  grid-template-columns: 100px 100px 100px;
  grid-template-rows: 100px 100px 100px;
}
效果如下:

1-1、百分比
上面可写成固定包裹层大小,单项使用百分比的形式,效果一样:
.container {
  width: 300px;
  height: 300px;
  display: grid;
  grid-template-columns: 33.33% 33.33% 33.33%;
  grid-template-rows: 33.33% 33.33% 33.33%;
}
1-2、repeat()
可使用 repeat() 函数,简化重复的值。上面百分比可简写为:
.container {
  width: 300px;
  height: 300px;
  display: grid;
  grid-template-columns: repeat(3, 33.33%);
  grid-template-rows: repeat(3, 33.33%);
}
repeat() 除了重复值,还可以重复某种模式:
.container {
  height: 300px;
  display: grid;
  grid-template-columns: repeat(2, 40px 50px 60px);
  grid-template-rows: repeat(3, 33.33%);
}
效果如下:

1-3、auto-fill
auto-fill 关键字表示自动填充,在行(或列)上容纳尽可能多的固定单元格。举个例子:
.container {
  width: 300px;
  height: 300px;
  display: grid;
  grid-template-columns: repeat(auto-fill, 70px);
  grid-template-rows: repeat(auto-fill, 120px);
}
上面代码表示在宽高 300px 的容器中,每列按 70px 宽度的单元格自动填充,直到容器不能放置更多的列,按 120px 高度的单元格自动填充,直到容器不能放置更多的行,如果填充后的高度不够,会压缩最后一列/行,效果如下:

1-4、fr
网格布局提供了 fr 关键字(fraction 的缩写,意为"片段")表示倍数,如果两列的宽度分别为 1fr 和 2fr,就表示后者是前者的两倍。举个例子:
.container {
  width: 300px;
  height: 300px;
  display: grid;
  grid-template-columns: 1fr 2fr;
  grid-template-rows: 1fr 1fr 1fr;
}
列指定了 2 列以 1:2 的比例自适应,行只指定了 3 个单元格的比例,剩下两个单元格会以最小高度显示,效果如下:

fr 也可以与具体尺寸一起使用:
.container {
  width: 300px;
  height: 300px;
  display: grid;
  grid-template-columns: 100px 1fr 2fr;
  grid-template-rows: 1fr 1fr 1fr;
}
列指定了第一列固定 100px,剩下两列以 1:2 的比例自适应,行指定了 3 个单元格平均分,效果如下:

1-5、minmax()
minmax() 传入最小值和最大值,指定单元格尺寸范围。举个例子:
.container {
  width: 300px;
  height: 300px;
  display: grid;
  grid-template-columns: 1fr 1fr minmax(30px, 0.5fr);
  grid-template-rows: 1fr 1fr 1fr;
}
列指定了第三列的宽度在 30px 到 0.5fr 之间,效果如下:

1-6、auto
auto 关键字表示由浏览器自己决定长度。举个例子:
.container {
  width: 300px;
  height: 300px;
  display: grid;
  grid-template-columns: 1fr 1fr auto;
  grid-template-rows: 1fr 1fr auto;
}
这里第 3 列与第 3 行指定 auto,使对应单元格以最小宽度/高度显示,效果如下:

1-7、网格线命名
可在 grid-template-[columns/rows] 中用方括号给对应位置的网格线命名,方便单项位置的存放:
.container {
  display: grid;
  grid-template-columns: [c1] 100px [c2] 100px [c3] auto [c4];
  grid-template-rows: [r1] 100px [r2] 100px [r3] auto [r4];
}
2、grid-template-areas
grid-template-areas 用于定义区域,一个区域由单个或多个单元格组成,一般结合子项的 grid-area 使用。可选值如下:
- <grid-area-name>ㅤ指定网格区域 grid-area 的名称;
- .ㅤ表示一个空的网格单元;
- noneㅤ表示没有定义网格区域。
举个例子:
<div class="container">
  <div class="item-a">1</div>
  <div class="item-a">2</div>
  <div class="item-b">3</div>
  <div class="item-c">4</div>
  <div class="item-d">5</div>
</div>
.container {
  width: 300px;
  height: 300px;
  display: grid;
  grid-template-columns: 1fr 1fr 1fr 1fr;
  grid-template-rows: auto;
  grid-template-areas:
    "a a a a"
    "b b . c"
    "d d d d";
}
.item-a {
  grid-area: a;
}
.item-b {
  grid-area: b;
}
.item-c {
  grid-area: c;
}
.item-d {
  grid-area: d;
}
效果如下:

3、[column/row]-gap | gap
- column-gap:设置列间距;
- row-gap:设置行间距;
- gap:列间距与行间距的简写。
举个例子:
.container {
  width: 300px;
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  grid-template-rows: repeat(3, calc((300px - 20px) / 3));
  /* 写法一 */
  column-gap: 10px;
  row-gap: 10px;
  /* 写法二 */
  gap: 10px 10px;
  /* 写法三 */
  gap: 10px;
}
效果如下:

4、grid-auto-flow
grid-auto-flow 决定子元素的排列方式,默认为 row。
4-1、row | column
- row:按"先行后列"的顺序排列;
- column:按"先列后行"的顺序排。
举个例子,默认 row 的排列如下:

设为 column:
.container {
  /* ... */
  grid-auto-flow: column;
}
效果如下:

4-2、[row/column] dense
- row dense:按"先行后列"的顺序排列,并尽可能紧密填满;
- column:按"先列后行"的顺序排,并尽可能紧密填满。
举个例子:
<div class="container">
  <div class="item item1">1</div>
  <div class="item item2">2</div>
  <div class="item">3</div>
  <div class="item">4</div>
  <div class="item">5</div>
  <div class="item">6</div>
  <div class="item">7</div>
  <div class="item">8</div>
  <div class="item">9</div>
</div>
.container {
  width: 300px;
  height: 300px;
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  grid-template-rows: auto;
}
.item1 {
  grid-column-start: 1;
  grid-column-end: 3;
}
.item2 {
  grid-column-start: 1;
  grid-column-end: 3;
}
没设 row dense 之前,效果如下:

设为 row dense:
.container {
  /* ... */
  grid-auto-flow: row dense;
}
效果如下:

column dense 同理。
4、justify-[content/items]
- justify-content:设置整个内容区域在包裹层中的水平位置;
- justify-items:设置单元格内容的水平位置。


5、align-[content/items]
- align-content:设置整个内容区域的垂直位置;
- align-items:设置单元格内容的垂直位置;


二、单项属性
1、grid-[column/row]-[start/end]
- grid-column-start:设置单项左边框所在的垂直网格线;
- grid-column-end:设置单项右边框所在的垂直网格线;
- grid-row-start:设置单项上边框所在的水平网格线;
- grid-row-end:设置单项下边框所在的水平网格线。

举个例子:
<div class="container">
  <div class="item item1">1</div>
  <div class="item item2">2</div>
  <div class="item">3</div>
  <div class="item">4</div>
  <div class="item">5</div>
  <div class="item">6</div>
</div>
.container {
  width: 300px;
  height: 300px;
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  grid-template-rows: 1fr 1fr 1fr;
}
.item1 {
  grid-column-start: 2;
  grid-column-end: 4;
}
.item2 {
  grid-row-start: 2;
  grid-row-end: 4;
}
效果如下:

这里除了使用纯数字指定是第几根网格线外,还可以使用 grid-template-[columns/rows] 中命名的网格线,也可以使用 span 关键字,表示水平跨越了多少个单元格,上面 .item1 的写法等同于:
.item1 {
  grid-column-start: 2;
  grid-column-end: span 2;
}
注意 span 后面要加空格。
2、grid-[column/row]
- grid-column:grid-column-start 和 grid-column-end 的合并简写;
- grid-row:grid-row-start 和 grid-row-end 的合并简写;
上面的写法等同于:
.item1 {
  grid-column: 2 / 4;
  /* 或 */
  grid-column: 2 / span 2;
}
.item2 {
  grid-row: 2 / 4;
}
3、grid-area
grid-area 指定单项放在哪个区域,有两种用法:
- 配合上面 grid-template-areas 使用区域名称;
- 指定垂直/水平网格线的起始/终止位置,控制单项存放的区域,格式为:
grid-area: <row-start> / <column-start> / <row-end> / <column-end>;
上面 grid-template-areas 的例子可用下面 grid-area 控制网格线的写法:
<div class="container">
  <div class="item-1">1</div>
  <div class="item-2">2</div>
  <div class="item-3">3</div>
  <div class="item-4">4</div>
  <div class="item-5">5</div>
</div>
.container {
  width: 300px;
  height: 300px;
  display: grid;
  grid-template-columns: 1fr 1fr 1fr 1fr;
  grid-template-rows: 1fr 1fr 1fr;
}
.item1 {
  grid-area: 1 / 1 / 2 / span 4;
}
.item2 {
  grid-area: 1 / 1 / 2 / span 4;
}
.item3 {
  grid-area: 2 / 1 / 3 / span 2;
}
.item4 {
  grid-area: 2 / 4 / 3 / span 1;
}
.item5 {
  grid-area: 3 / 1 / 4 / 5;
}
效果如下:

4、[justify / align / place]-self
- justify-self:设置单项单元格内容的水平位置;
- align-self:设置单项单元格内容的垂直位置;
- place-self:align-self 与 justify-self 的合并简写。


三、Grid 布局的兼容性

四、总结
