Skip to content
This page has been auto-translated and may contain errors.View in English

网格

docs.scrimba.com

你有一个页面,顶部有标题栏,左侧有侧边栏,中间有主内容区,底部有页脚。你可以嵌套多个容器,并将每个容器推到适当的位置,这样做是可行的,直到你需要改变布局的那一天。CSS 网格正是为了解决这种问题而构建的:同时用行和列来布局页面。一旦你能将布局描述为一组轨道,大部分关于包装器和浮动的调整就消失了。

网格容器

将任何元素设置为 display: grid 可以将其变成网格。该元素成为 网格容器,它的直接子元素成为吸附到你定义的网格中的项目。仅仅设置 display: grid 不会做太多事情,所以你还需要告诉它你想要多少列和行,以及每一个应该有多大。

你用 grid-template-columns 描述列,用 grid-template-rows 描述行。列表中的每个值都是一个轨道,一个轨道是单个列或行:

css
.layout {
  display: grid;
  grid-template-columns: 200px 1fr;   /* 两列:一个固定的,然后是剩余部分 */
  grid-template-rows: 80px 400px;     /* 两行:一个短的,然后是一个高的 */
}

在元素上设置 display: grid 使其成为 网格容器,它的直接子元素成为铺放在你定义的网格上的网格项目。这是 flexbox 的二维对应物,flexbox 一次沿单个轴布置项目。Flexbox 问的是"这一行(或列)的项目应该如何共享空间?";网格问的是"这些项目在一组行和列中的位置在哪里?"。

你用 grid-template-columnsgrid-template-rows 定义轨道。每个值都是一个轨道,一个列或行,这些值可以混合单位:固定长度、百分比,或者你在下一节中将学到的灵活的 fr 单位。

css
.layout {
  display: grid;
  grid-template-columns: 200px 1fr;   /* 固定侧边栏,灵活主列 */
  grid-template-rows: auto 1fr auto;  /* 标题尺寸适应内容,主列填充,页脚尺寸适应内容 */
}

display: grid 在元素上建立了一个 网格格式化上下文:它成为网格容器,它的直接子元素成为网格项目,曾经管理这些子元素的正常流规则不再适用。文本节点被包装在匿名项目中,floatvertical-align 这样的属性对项目不再有任何影响,因为它们的放置现在是网格的工作。

flexbox 的区别是维度,这是首先要做对的决定。Flexbox 是一维的:它沿主轴分配项目,换行作为副作用,但它不会在交叉轴上将内容对齐到匹配的网格。网格是二维的:它同时定义行和列,所以项目的行位置和列位置是独立设置的,项目同时在两个轴上对齐。你用 grid-template-columnsgrid-template-rows 定义显式轨道,每个值产生一个轨道:

css
.layout {
  display: grid;
  grid-template-columns: 200px 1fr;   /* 两个显式列轨道 */
  grid-template-rows: auto 1fr auto;  /* 内容、填充、内容 */
}

auto 关键字将轨道尺寸设置为其内容;1fr 吸收剩余空间,这是下一节将构建的机制。

Juno网格容器 在元素上添加 display: grid,它就变成了网格容器,它的直接子元素作为项目位于其中。然后你用 grid-template-columnsgrid-template-rows 说明你想要多少列和行,每个轨道一个值。没有定义那些轨道,什么都不会排列,所以这总是你的第一步。
Juno网格容器 Flexbox 一次沿一个轴工作;网格同时在行和列中工作,这就是为什么页面布局在网格中感觉更自然。设置 display: grid,然后在 grid-template-columnsgrid-template-rows 中列出你的轨道。当你在二维安排中放置东西时,选择网格,当你排列一行或一列时,选择 flexbox。
Juno网格容器display: grid 创建了一个网格格式化上下文,所以子元素停止遵守正常流,floatvertical-align 在它们上面悄悄停止起作用。与 flexbox 的真正分裂是维度:flex 沿一个轴分配,网格同时在两个轴上放置。当行和列位置都有意义时选择网格,当只有一个轴时选择 flex。

灵活轨道

固定像素列还可以,但大多数布局需要列平均共享空间并随屏幕增长。这正是 fr 单位 的用途。一个 fr 意味着一份剩余空间,所以三列各 1fr 各占容器的三分之一:

css
.gallery {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;   /* 三个相等的列 */
}

输入三次 1fr 会很乏味,所以 repeat() 为你写好。gap 设置轨道之间的空间,所以你不用为每个项目都设置边距:

css
.gallery {
  display: grid;
  grid-template-columns: repeat(3, 1fr);   /* 相同的三个相等列 */
  gap: 16px;                               /* 行列之间的空间 */
}

fr 单位衡量容器中固定尺寸和间隙扣除后的可用空间的份额。三列各 1fr 各得三分之一;2fr 1fr 对以二比一分割空间。因为 fr 分割的是剩余部分,列在容器增长或缩小时保持比例,这是固定长度做不到的。

两个辅助功能使轨道列表可读。repeat(3, 1fr) 展开为 1fr 1fr 1frgap 在一个属性中设置装订线(当你想让它们不同时用 row-gapcolumn-gap):

css
.gallery {
  display: grid;
  grid-template-columns: repeat(3, 1fr);   /* 三个相等的列 */
  gap: 16px;                               /* 16px 装订线,行和列 */
}

装订线用 gap 设置,不是项目上的边距。因为 gap 只在轨道之间应用,从不在外边缘,网格与其容器齐平,而项目保持均匀间距。

fr 单位是一个 灵活长度:它表示容器可用空间的一个分数,是在固定轨道尺寸、内容大小的轨道和间隙解决后剩余的空间。2fr 1fr 1fr 轨道列表将一半的可用空间分给第一列,其他两列各四分之一。微妙之处在于 fr 分配的是 可用 空间,不是总宽度,所以持有宽内容的轨道会超过其 fr 份额,除非你限制它,这就是最后一节中 minmax() 的用武之地。

repeat() 是写一段轨道的紧凑方式:repeat(3, 1fr)1fr 1fr 1fr,它是组合的,所以 repeat(2, 200px 1fr) 产生四个轨道。gapgrid-gap 的现代名称)只在轨道之间设置装订线,从不在网格外部,所以它不与容器自己的填充冲突。

css
.gallery {
  display: grid;
  grid-template-columns: repeat(3, 1fr);   /* 三个相等的列 */
  gap: 16px 24px;                          /* 16px 行间隙,24px 列间隙 */
}

注意两值的 gap:行间隙优先,然后是列间隙。单个值应用于两者。

Juno灵活轨道 一个 fr 是一份剩余空间,所以 1fr 1fr 1fr 给你三个相等的列。使用 repeat(3, 1fr) 这样你就不用一遍遍输入相同的值,用 gap 表示轨道之间的空间。一旦我理解了 fr,我就停止使用永远加不起来的百分比了。
Juno灵活轨道fr 分割容器中的可用空间,所以比例列在页面调整大小时保持比例。依赖 repeat() 保持轨道列表简短,用 gap 设置装订线而不是逐项边距。记住 gap 只在轨道之间,从不在外边缘,这通常正是你想要的。
Juno灵活轨道fr 分割可用空间,不是总宽度,所以持有宽内容的轨道会溢出其份额,直到你用 minmax() 限制它。repeat() 是组合的,所以 repeat(2, 200px 1fr) 是四个轨道。两值 gap 读作行间隙优先,然后列间隙,这会绊倒假设它匹配顺时针简写的人。

放置项目

默认情况下,每个项目按顺序放入下一个空闲单元,每个单元一个。当你想让一个项目占用多于一个单元时,你用 grid-columngrid-row 说明。轨道之间的线从 1 开始编号,所以 grid-column: 1 / 3 表示"从线 1 开始,在线 3 结束",覆盖两列:

css
.featured {
  grid-column: 1 / 3;   /* 从列线 1 到线 3,两列宽 */
}

如果你不想数线,span 说明从项目着陆处开始覆盖多少个单元:

css
.featured {
  grid-column: span 2;   /* 覆盖两列,从这个项目所在的任何地方开始 */
}

网格放置用 网格线 描述,即轨道之间和周围的编号线。一个三列网格有四条列线,14grid-column: 1 / 3 将项目放在线 1 到线 3,所以它跨越前两列;grid-row 在垂直方向做同样的事。span 关键字避免计数:grid-column: span 2 从项目放置的任何地方覆盖两列。

对于整个页面布局,命名区域比计数线更易读。grid-template-areas 让你将布局画成一组命名单元,然后将每个项目分配给一个名称:

css
.page {
  display: grid;
  grid-template-columns: 200px 1fr;
  grid-template-areas:
    "sidebar header"
    "sidebar main";     /* 侧边栏通过重复其名称跨越两行 */
}

.page > header { grid-area: header; }
.page > aside  { grid-area: sidebar; }
.page > main   { grid-area: main; }

放置在 网格线 上运行,即每个轨道周围的编号边界(n 轨道轴有 n + 1 线)。grid-column: 1 / 3 设置开始和结束列线,跨越它们之间的轨道;grid-column: span 2 设置跨度而不固定开始,让自动放置算法选择它着陆的地方。负线号从末尾计数,所以 grid-column: 1 / -1 总是跨越全宽,不管列数多少,这是写全出血行的持久方式。

grid-template-areas 命名单元,所以你按名称而不是行号放置项目。每个字符串都是一行,每个令牌都是一个单元,跨单元重复令牌使一个项目跨越它们。空格格式化的块兼作布局的可读图表:

css
.page {
  display: grid;
  grid-template-columns: 200px 1fr;
  grid-template-rows: auto 1fr;
  grid-template-areas:
    "sidebar header"
    "sidebar main";     /* 侧边栏占据第一列的两行 */
}

.page > aside { grid-area: sidebar; }

两个规则保持诚实:每个行字符串必须有相同的单元数,一个名称的单元必须形成坚实的矩形,否则声明无效并被忽略。. 令牌标记一个你想故意留空的单元。

Juno放置项目 项目默认填充一个单元,你用 grid-columngrid-row 让一个跨越单元。线号从 1 开始,所以 grid-column: 1 / 3 覆盖两列,或者如果你不想数,使用 span 2。在你命名任何单个区域之前,这足以构建大多数布局。
Juno放置项目 用网格线放置项目:grid-column: 1 / 3 跨越两列,span 2 在没有从固定开始计数的情况下做到这一点。对于整个页面,grid-template-areas 让你命名区域并一目了然地读取布局,这比手工跟踪线号要好得多。跨单元重复一个名称,该区域就跨越它们。
Juno放置项目 线可以是负数,所以 grid-column: 1 / -1 不管有多少列都跨越全宽,这是可靠的全出血技巧。使用 grid-template-areas,每个行字符串都需要相等的单元,每个命名区域必须是坚实的矩形,否则整个声明被删除。使用 . 令牌故意将一个单元留空。

不使用媒体查询的响应式网格

一行卡片应该在宽屏上显示三个并排,在平板上显示两个,在手机上显示一个。你可以仅用网格做到这一点,不需要写任何断点。技巧是一行:

css
.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 16px;
}

从里到外读。minmax(200px, 1fr) 意味着每列至少 200px 最多一份空间。auto-fit 然后将尽可能多的这些列放入容器有房间的地方。宽屏幕,更多列放进去;窄屏幕,较少列,卡片自动重流。

不使用单个媒体查询重流卡片的模式是 repeat(auto-fit, minmax(200px, 1fr))。每个部分做一项工作。minmax(200px, 1fr) 定义一个永不小于 200px 永不大于一份 fr 份额的轨道。auto-fit 重复该轨道尽可能多次以适应当前宽度,所以列数响应容器而不是响应固定断点。

css
.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 16px;
}

200px 下限是你的真实控制:它是卡片在网格降至较少列之前允许获得的最窄。这与 响应式设计 中的流体技术自然配对,它通常替代你否则为卡片网格写的媒体查询。

repeat(auto-fit, minmax(200px, 1fr)) 是规范的响应式网格习语,每部分都赚取它的位置。minmax(min, max) 定义一个有下限和上限的轨道:minmax(200px, 1fr) 永不低于 200px 永不高于一份可用空间。200px 下限设置重流阈值,网格获得或失去列的宽度。

auto-fitauto-fill 之间的选择是值得知道的部分。两者都计算多少 minmax() 轨道适应容器。auto-fill 在网格中保持剩余的空轨道,所以半空的卡片行保持其列宽。auto-fit 将那些空轨道折叠为零宽度,所以现在的项目扩展填充行:

css
.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));   /* 项目拉伸填充 */
  gap: 16px;
}

注意一个边缘:minmax(200px, 1fr) 可以溢出比 200px 加间隙更窄的容器,因为下限是硬最小值。minmax(min(200px, 100%), 1fr) 通过让下限在非常小的屏幕上降至容器宽度来防范这种情况。为了在设备上可靠地调整下限,尺寸单位 覆盖长度单位如何解析。

Juno不使用媒体查询的响应式网格repeat(auto-fit, minmax(200px, 1fr)) 让卡片自动重流,不需要断点。每列至少 200px 最多一份空间,auto-fit 尽可能多地装入。改变 200px,你改变有多少卡片并排。
Juno不使用媒体查询的响应式网格 一行,repeat(auto-fit, minmax(200px, 1fr)),替代一堆卡片网格媒体查询。200px 下限是杠杆:它决定卡片在网格降列之前变得多窄。在为真正重流的卡片网格写断点之前,选择这个。
Juno不使用媒体查询的响应式网格auto-fit 折叠空轨道所以现在项目拉伸填充;auto-fill 保持空轨道并留下间隙,这是它们之间的全部区别。注意溢出情况:硬 200px 下限会在非常窄的容器上溢出,所以 minmax(min(200px, 100%), 1fr) 是更安全的下限。下限设置你的重流点,所以选择它时考虑最小合理卡片。

显式和隐式网格

到目前为止的一切都定义了 显式网格:你在 grid-template-columnsgrid-template-rows 中命名的轨道。当项目着陆在该定义网格之外时,浏览器创建轨道来持有它们,那些形成 隐式网格。向三列、一行的显式网格添加第四个项目,它开始新的、隐式创建的第二行。隐式轨道默认尺寸为 auto,这就是为什么隐式行只有其内容的高度,除非你另外说明。

grid-auto-rows 设置那些隐式行的尺寸(grid-auto-columns 设置隐式列),所以卡片网格的行数你无法预测仍然得到均匀行:

css
.gallery {
  display: grid;
  grid-template-columns: repeat(3, 1fr);   /* 显式:三列 */
  grid-auto-rows: 240px;                   /* 隐式行各 240px */
  gap: 16px;
}

对齐然后决定项目和轨道在其单元内坐的位置。justify-items 沿行(内联)轴对齐项目,align-items 沿列(块)轴对齐,各接受 startendcenterstretch(默认,填充单元)。place-items 是两者的简写,块轴优先:

css
.gallery {
  place-items: center;   /* align-items 和 justify-items,两者都居中 */
}

minmax() 也接受内容关键字 min-contentmax-contentmin-content 是轨道不溢出可以达到的最小(大致是最长不可中断的词),max-content 是内容在没有任何东西强制它换行时想要的尺寸。像 minmax(min-content, max-content) 这样的轨道在那些界限内尺寸化为内容。subgrid 让嵌套网格继承其父级的轨道线,所以子网格的列与外部网格对齐,而不是定义自己的。

在网格与 flex 决定上,清晰的规则是这样的。如果你在一个轴上安排内容并让它作为后果换行,使用 flexbox:导航栏、一行标签、工具栏。如果你将内容放入必须在两个轴上对齐的行和列中,使用网格:页面框架、卡片库、带有对齐标签和字段的表单。它们组合得很好,所以网格单元通常持有 flex 容器,那种组合覆盖你将构建的大多数布局。

Juno显式和隐式网格 你声明的轨道是显式网格;浏览器添加的任何东西来持有溢出项目是隐式网格,那些额外轨道是 auto 大小的,直到 grid-auto-rowsgrid-auto-columns 另外说明。对齐在 justify-itemsalign-items 上,place-items 作为简写,stretch 作为默认。决定是一个轴带换行意味着 flexbox,两个必须对齐的轴意味着网格,它们愉快地嵌套。