想象你正在装修一套新房子。过去用 Flexbox 时,你只是在客厅里摆一排鞋柜:所有东西都沿着一条线排列,简单但很难同时管好整屋的横纵方向。CSS Grid 则不同,它让你先在一张图纸上画好整个户型的二维网格——横着有几面墙、竖着有几面墙、每个房间叫什么名字。等你把“客厅”“书房”“卫生间”标在图纸上,再把网页里的元素当作家具,一件件“搬”进对应的房间。这就是 Grid 布局:页面在水平和垂直两个方向上同时被安排,而“给房间贴名牌”的技术,就是网格区域命名。 更妙的是,这张户型图不是死的。你可以只定义区域名,让浏览器根据屏幕宽度自动增加或减少房间;可以留出走廊;还可以让 Flexbox 去处理房间内的一排小物件。CSS Grid 是二维布局的“总设计师”,Flexbox 是房间内“擅长摆一条线”的管家。掌握它,你就从“摆鞋柜”升级为“设计整屋”。

岗位关联

对 AI 从业者:AI 要生成可控、可维护的前端页面,必须理解 Grid 的二维骨架,尤其是“区域命名”这种接近自然语言的描述方式,能让模型像读户型图一样理解页面布局。

目录
  1. 可视化演示
  2. 网格基础:先看懂户型图
  3. 行列定义:画出承重墙
  4. 区域命名:给房间贴名牌
  5. 间距控制:给房间留走廊
  6. 自动排列:让机器人摆家具
  7. 响应式设计:会变形的户型图
  8. 与Flex配合:两个工种分工
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示CSS Grid的行列定义、区域命名与gap间距,通过交互控件切换不同网格模板,实时观察二维布局变化。

本节配有交互动效(进入视口后自动加载,约 3KB)。

CSS Grid区域命名 grid-template-areas定义区域 gap控制间距 display:grid

网格基础:先看懂户型图

拿到一套新房,设计师不会急着买家具,而是先在图纸上画横平竖直的参考线。CSS Grid 的第一步也是这样:在父元素上写 display: grid;,这个元素就变成了“网格容器”,它里面的直接子元素叫“网格项目”。网格容器里的横线和竖线叫“网格线”,两条相邻网格线之间的长条区域叫“轨道”,横轨和竖轨交叉出的最小格子叫“单元格”。

小区里每栋楼都有楼号,户型图上的参考线也同样重要。有了网格线和轨道,你才能准确说“把书架放在第 2 条竖线到第 3 条竖线之间”。Grid 和 Flexbox 最大的差别就在这里:Flex 只有一条“队伍”,而 Grid 同时管理横队和纵队。理解网格线,是后面所有定位的基础。

一个简单的网格容器需要定义列和行,否则所有项目会默认挤在一列里。无论横竖,线的数量都与轨道数有固定关系:

网格线只是骨架,真正让户型图实用的是“每面墙之间的距离”,这一步,就是下一节要学的行列定义。

网格线数=轨道数+1\text{网格线数} = \text{轨道数} + 1
公式说明:轨道是两条线之间的“房间排”,所以横线或竖线的数量总比轨道数多 1。
  1. 网格线数户型图上所有横向或纵向参考线的总数。
    网格线数\text{网格线数}
  2. 轨道数两条相邻网格线之间的带状区域数量。
    轨道数\text{轨道数}
  3. 加一最外侧还有两条外边框线,所以线比轨道多一条。
    轨道数+1\text{轨道数} + 1

行列定义:画出承重墙

参考线画好了,还得决定每面墙放在哪里、房间有多宽。CSS Grid 用 grid-template-columns 定义竖轨道的宽度,用 grid-template-rows 定义横轨道的高度。这就像在户型图上标注“客厅宽 4 米、卧室宽 3 米”,数值单位可以用像素、百分比,也可以用弹性单位 fr。

写 grid-template-columns: 100px 1fr 2fr; 时,第一列固定 100px,剩余空间先扣除列间距,再按 1 : 2 分给后两列。fr 是 Grid 特有的弹性单位,专门用于“按比例瓜分剩余空间”;repeat(3, 1fr) 则表示三间一样大的标准间,不用反复写同一个长度。这个分配逻辑可以写成:

墙画好了,接下来最关键的步骤,就是给这些房间贴上门牌号,让元素知道自己该住哪一间。

wi=fif1+f2+⋯+fn×(W−(n−1)×g)w_i = \frac{f_i}{f_1 + f_2 + \cdots + f_n} \times \left(W - (n-1) \times g\right)
公式说明:每个弹性列的宽度,等于它的弹性系数占总系数的比例,再乘上“去掉所有列间距后的剩余总宽”。
  1. W网格容器的总宽度,相当于整套房子的可用总面积。
    WW
  2. f_i当前列声明的 fr 系数,比如 1fr 中系数是 1。
    fif_i
  3. 分母求和把所有列的弹性系数加起来,得到总份数。
    f1+f2+⋯+fnf_1 + f_2 + \cdots + f_n
  4. 扣除间距总宽度减去 n-1 个列间距,剩下的才是真正能分给房间的面积。
    W−(n−1)×gW - (n-1) \times g
  5. 按比例分配用当前系数除总份数,再乘剩余宽度,就得到该列宽度。
    fif1+f2+⋯+fn×(W−(n−1)×g)\frac{f_i}{f_1 + f_2 + \cdots + f_n} \times \left(W - (n-1) \times g\right)

区域命名:给房间贴名牌

户型图上只有“格子”还不够,你还需要告诉别人“这里是厨房,那里是书房”。CSS Grid 的 grid-template-areas 就是用来画这张“字符地图”的:一行字符串代表网格的一行,每个名字代表一个单元格。比如:

写完地图后,子元素只要写 grid-area: main;,就会自动落到名为 main 的区域里。区域名必须组成一个完整矩形,用英文句号 . 表示“这个位置不放东西”。这种命名方式让布局像户型图一样一目了然,也方便后续在响应式设计中整体换图纸。一个命名区域在数学上可以看作一组连续的单元格:

给房间贴好名牌后,元素就能准确入住。但房间之间还需要留一点空隙,这就是接下来要讲的间距控制。

Amain={(r,c)∣rs≤r<re,  cs≤c<ce}A_{\text{main}} = \{(r, c) \mid r_s \le r < r_e,\; c_s \le c < c_e\}
公式说明:区域 main 是行号从 r_s 到 r_e-1、列号从 c_s 到 c_e-1 的所有单元格的集合。
  1. A_main名为 main 的网格区域,相当于户型图上画了斜线的那块房间。
    AmainA_{\text{main}}
  2. 二元组 (r, c)每个单元格的行号与列号,就像房间的“门牌坐标”。
    (r,c)(r, c)
  3. 起始行与列r_s、c_s 是区域左上角起始线。
    rs,  csr_s,\; c_s
  4. 结束行与列r_e、c_e 是区域右下角结束线,采用“含左不含右”规则。
    rs≤r<re,  cs≤c<cer_s \le r < r_e,\; c_s \le c < c_e
css4 行
grid-template-areas:  "header header header"  "sidebar main   main"  "footer footer footer";

间距控制:给房间留走廊

再紧凑的户型,也不会让两堵墙零距离贴在一起,总要留步行通道。CSS Grid 的 gap 就是这条走廊,专门控制网格轨道之间的间距。gap: 10px 20px; 表示行间距 10px、列间距 20px;也可以分开写成 row-gap 和 column-gap。

与 margin 不同,gap 是网格容器内部的“公共走廊”,不会算进某个房间的宽度里,也不会让容器被内容撑破。负的 gap 是不合法的,浏览器会忽略它。布局里一旦有多个轨道,计算总占用宽度时,必须把这些走廊也加进去:

走廊留好了,接下来要解决的是:如果家具没有被指定房间,它们该怎么自动摆放?

W总=∑i=1nwi+(n−1)×gW_{\text{总}} = \sum_{i=1}^{n} w_i + (n-1) \times g
公式说明:所有列宽相加,再加上列与列之间的 n-1 个间距,才是整套户型实际占用的总宽度。
  1. 求和符号把每一列的房间宽度全部加起来。
    ∑i=1nwi\sum_{i=1}^{n} w_i
  2. n网格的列数,也就是竖轨道数量。
    nn
  3. n-1列与列之间的走廊数量,比列数少一个。
    n−1n-1
  4. g单个列间距,由 column-gap 定义。
    gg

自动排列:让机器人摆家具

不是每件家具都要你亲手指定房间。网格里没有写 grid-area 的项目,会自动按照顺序填进单元格,这个行为由 grid-auto-flow 控制。默认值是 row,像机器人先横着把第一排摆满,再换到第二排;设为 column 则会先竖着摆。

如果预先定义的行或列不够用,Grid 会自动创建额外轨道,这些轨道叫“隐式轨道”。它们就像户型图外临时搭出来的储物间。dense 关键字则让机器人更聪明:遇到空洞时,先拿后面的家具回头补上,让房间更紧凑,但视觉顺序可能不再完全等于源代码顺序。自动排列需要的总行数可以这样估计:

自动摆放解决了“塞得下”的问题,但手机和电脑屏幕大小不同,户型图还需要能变形。

R=⌈NC⌉R = \left\lceil \frac{N}{C} \right\rceil
公式说明:把项目总数除以每行轨道数,再向上取整,得到至少需要多少行。
  1. R需要的行数,包括自动生成的隐式行。
    RR
  2. N需要自动排列的网格项目总数。
    NN
  3. C每一行能放下的列轨道数。
    CC
  4. 向上取整如果除不尽,必须多开一行来放剩下的项目。
    ⌈NC⌉\left\lceil \frac{N}{C} \right\rceil

响应式设计:会变形的户型图

同一个家,在手机上是紧凑的一居室,在电脑上就是开阔的大平层。CSS Grid 的响应式设计,就是让户型图根据屏幕尺寸自动调整房间数量和大小。最常见的工具是媒体查询:在小屏幕上换一套 grid-template-areas,在大屏幕上再换一套。

另一个利器是 minmax(200px, 1fr):它规定轨道“最小不能小于 200px,最大可以按比例伸展”。配合 auto-fit 或 auto-fill,浏览器能自动决定一排放几列。auto-fill 倾向于尽量多放空轨道,auto-fit 倾向于把空轨道折叠后让项目变宽。auto-fill 能放下的列数大致可以这样估算:

户型图会变形之后,整个页面骨架就活了。但房间内部的细节,还需要另一个工种来配合。

N列=⌊W+gwmin⁡+g⌋N_{\text{列}} = \left\lfloor \frac{W + g}{w_{\min} + g} \right\rfloor
公式说明:每个项目至少占 w_min + g 的宽度,用容器总宽加一个间距去除,向下取整,就是最多能放下的列数。
  1. N_列当前屏幕宽度下最多能容纳多少列。
    N列N_{\text{列}}
  2. W容器可用总宽度。
    WW
  3. w_min每个轨道的最小宽度,比如 minmax(200px, 1fr) 中的 200px。
    wmin⁡w_{\min}
  4. g列间距,需要加进每个轨道占用的总宽度里。
    W+g与wmin⁡+gW + g \quad \text{与} \quad w_{\min} + g
  5. 向下取整放不下最后一个完整轨道时,就不放,保证不会挤爆。
    ⌊W+gwmin⁡+g⌋\left\lfloor \frac{W + g}{w_{\min} + g} \right\rfloor

与Flex配合:两个工种分工

CSS Grid 是整屋设计师,负责“哪里放客厅、哪里放卧室”的二维大结构;Flexbox 是室内整理师,负责“书架上摆一排书、导航栏里排一排按钮”的一维小细节。两者不是对手,而是搭档。

在实际页面中,最常见的策略是“外层 Grid 拼骨架,内层 Flex 做排列”。比如整个后台页面用 grid-template-areas 分成顶栏、侧边栏、主内容区,而主内容区内部的一排操作按钮,再用 display: flex 来对齐。这样各司其职,代码清晰又灵活:

现在你已经有了完整的“装修工具箱”:先画户型图,再定义行与列,给房间命名,留出走廊,最后让 Grid 和 Flex 分工协作。下一次看到复杂页面,你就不再是“摆鞋柜”的新手,而是能画出整屋图纸的设计师了。

---

页面布局=G⏟二维网格骨架+F⏟一维对齐细节\text{页面布局} = \underbrace{G}_{\text{二维网格骨架}} + \underbrace{F}_{\text{一维对齐细节}}
公式说明:一个完整的响应式页面布局,等于 Grid 负责的二维骨架,加上 Flex 负责的一维细节。
  1. 页面布局最终要实现的完整页面结构。
    页面布局\text{页面布局}
  2. GGrid 容器,用来规划整张户型图的区域。
    GG
  3. FFlex 容器,用来处理一个条带内的对齐和间距。
    FF
  4. 相加在 Grid 区域内部嵌套 Flex,两者组合使用。
    G+FG + F

AI 应用场景

场景一:AI 根据自然语言生成前端页面

阶段:生成式代码生成 / 前端智能体

解决的问题:传统方式手写响应式布局时,需要反复计算屏幕宽度、手动嵌套容器,遇到多端适配很容易“牵一发而动全身”。AI 助手如果能听懂“顶部通栏、左侧栏、主内容、底部通栏”,直接输出 grid-template-areas,就等于把户型图翻译成了 CSS 代码。

具体案例:开发一个在线页面试图工具,用户输入“我要三列卡片”,AI 自动生成 repeat(3, 1fr) 的网格;用户改成“中间主内容,右边广告栏”,AI 便生成带命名区域的 Grid 模板,前端工程师只需微调间距。

场景二:设计稿自动转代码

阶段:多模态识别 / 图像到代码

解决的问题:从 Figma 或 Sketch 截图转 HTML/CSS 时,传统模板很难还原复杂的二维区域关系。AI 目标检测把设计稿切分成矩形块后,可以映射为 grid-template-areas 的区域名,再翻译成行列模板,避免靠绝对定位硬拼。

具体案例:上传一张后台管理页面截图,AI 识别出 header、sidebar、main、footer 四个大块,输出标准 CSS Grid 布局代码;响应式断点也由 auto-fit 和 minmax 自动生成,人工只需要检查语义和颜色。

Python 演示

python57 行
# -*- coding: utf-8 -*-# Parse grid-template-areas strings and generate CSS Grid codeimport re  class GridAreaParser:    def __init__(self, css_areas: str):        self.css_areas = css_areas        self.area_rows = []        self.area_map = {}        self.row_count = 0        self.col_count = 0        self._parse()     def _parse(self):        lines = [ln.strip() for ln in self.css_areas.strip().splitlines() if ln.strip()]        rows = [re.split(r"\s+", ln) for ln in lines]        self.row_count = len(rows)        self.col_count = max(len(r) for r in rows)        for r, row in enumerate(rows):            for c, name in enumerate(row):                if name == ".":                    continue                if name not in self.area_map:                    self.area_map[name] = [r, c, r + 1, c + 1]                else:                    e = self.area_map[name]                    e[0] = min(e[0], r)                    e[1] = min(e[1], c)                    e[2] = max(e[2], r + 1)                    e[3] = max(e[3], c + 1)        self.area_rows = rows     def to_css(self):        lines = [            "display: grid;",            "grid-template-columns: repeat({}, 1fr);".format(self.col_count),            "grid-template-rows: repeat({}, 1fr);".format(self.row_count),            "grid-template-areas:",        ]        for row in self.area_rows:            lines.append("  \"{}\";".format(" ".join(row)))        for name in self.area_map:            r1, c1, r2, c2 = self.area_map[name]            lines.append(".{} {{ grid-area: {} / {} / {} / {}; }}".format(name, r1 + 1, c1 + 1, r2, c2))        return "\n".join(lines) + "\n"  if __name__ == "__main__":    layout = """      header header header      sidebar main   main      footer footer footer    """    parser = GridAreaParser(layout)    print("rows:", parser.row_count, "cols:", parser.col_count)    print(parser.to_css())
目的:把一个“字符画式”的 grid-template-areas 字符串解析成带行列坐标的 CSS Grid 代码,方便 AI 或工具自动生成前端布局。
  1. rows = [re.split(r"\s+", ln) for ln in lines]把户型图的每一行文字拆成单元格名字,变成二维数组。
  2. if name not in self.area_map:第一次遇到区域名时,记录它的起点和终点坐标。
  3. e[0] = min(e[0], r)不断向外扩展边界,让同名区域最终围成一个完整矩形。
  4. lines.append("grid-template-areas:")生成 CSS 里最像“字符地图”的属性。
  5. ".{} {{ grid-area: {} / {} / {} / {}; }}".format(...)把每个区域名转换为四线定位,对应 grid-row-start / grid-column-start / grid-row-end / grid-column-end。

常见误区

误区我只要给父元素加 `display: grid;`,里面的子元素就应该自动排成整齐的网格。

正解display: grid; 只是把父元素变成网格容器,像拿到了空白户型图。如果不写 grid-template-columns 和 grid-template-rows,Grid 只会按默认的自动排列规则处理,通常所有项目挤在一列里。真正的网格形状必须由列和行模板定义。

为何常见:很多教程直接展示“容器 + 列模板”的完整代码,初学者把列模板误当成了默认行为。

误区写上 `grid-area: main;` 就是在代码里创建一个叫 main 的大区域,并给它设置尺寸。

正解grid-area: main; 的含义是“把当前元素放进名为 main 的网格区域”,区域本身的大小由 grid-template-areas 和行列轨道决定。区域名是门牌号,不是房间面积。

为何常见:属性名里有 “area”,很容易让人联想到“划分区域”,但它本质上是一个“定位指令”。

误区元素之间距离 10px,用 `margin-right: 10px;` 加在每个项目上,不也一样吗?

正解margin 属于项目的外边距,会改变轨道内部的占用空间,搞不好还会让最后一个项目被挤到下一行。gap 是网格容器专门设置的“走廊”,不属于任何轨道,也不影响项目本身的宽度,是 Grid 推荐的做法。

为何常见:在 Flexbox 时代习惯用 margin 做间距,到了 Grid 会自然地延续旧习惯。

误区Flexbox 也能让元素一行排开,也能换行,那我直接用 Flexbox 不就行了?

正解Flexbox 本质是“一维排列”,要靠嵌套才能模拟二维结构;Grid 天然支持“行和列同时控制”,适合整页级的二维骨架。两者各有所长,正确姿势是“外 Grid 内 Flex”。

为何常见:简单页面用 Flexbox 看起来也能完成布局,直到遇到复杂后台或响应式大屏才会发现二维控制有多重要。

面试题

Q1. 在 CSS Grid 中,创建网格容器的属性是?

单选题 · easy

  • A.display: grid← 正确答案
  • B.display: flex
  • C.position: grid
  • D.display: block

答案:A。display: grid; 让元素成为网格容器,相当于拿到一张空白户型图。flex 创建的是弹性容器,position: grid 不存在,block 只是普通块级元素。

Q2. grid-template-columns: 100px 1fr 2fr; 中 1fr 2fr 的含义是?

单选题 · easy

  • A.固定 1px 和 2px
  • B.按 1:2 分剩余空间← 正确答案
  • C.重复一至两次
  • D.取值 1px 和 2px

答案:B。第一列固定 100px,剩下空间扣除间距后按 1:2 分给后两列。fr 是弹性单位,不是 1px 这样的固定长度,也不是重复次数。

Q3. 在 grid-template-areas 字符串中,英文句号 . 代表什么?

单选题 · easy

  • A.对应位置留白← 正确答案
  • B.代表一条网格线
  • C.代表项目默认名
  • D.占满全部空间

答案:A。.` 表示“这个单元格不放任何东西”,相当于户型图上的空地和储物间。它不是网格线,也不是项目的名字。

Q4. 下面哪种写法能生成三列等宽布局?

单选题 · mid

  • A.repeat(3, 1fr)← 正确答案
  • B.1fr repeat(3)
  • C.grid-repeat(3)
  • D.columns: 3 1fr

答案:A。repeat(3, 1fr) 表示三列等分剩余空间,是标准写法。其余选项语法错误或混淆了 columns 属性。

Q5. grid-template-areas 中,同一个命名区域必须是什么形状?

单选题 · mid

  • A.任意形状多边形
  • B.一个完整矩形← 正确答案
  • C.只能是一条线
  • D.必须是正方形

答案:B。CSS 规范要求命名区域必须组成单个完整矩形,L 形或圆形都无法被识别。就像户型图上,一个房间不能隔成两块不相连的碎片。

Q6. 以下哪些写法可以设置网格间距?

多选题 · mid

  • A.gap: 10px 20px← 正确答案
  • B.row-gap: 10px; column-gap: 20px← 正确答案
  • C.grid-gap: 20px← 正确答案
  • D.margin-gap: 10px

答案:ABC。gap 是标准简写,row-gap 和 column-gap 分开设置,grid-gap 是早期浏览器的兼容写法。margin-gap 不存在,间距不能靠 margin 冒充。

Q7. 关于 grid-auto-flow: dense,下列说法正确的是?

多选题 · mid

  • A.能回填前面的空洞← 正确答案
  • B.会改变 DOM 结构
  • C.可能改变视觉顺序← 正确答案
  • D.只能用于 row 方向

答案:AC。dense 允许后面较小的项目回头填充前面的空洞,所以视觉顺序可能与源码顺序不同,但 DOM 结构不会被修改。它不是只能在 row 方向使用。

Q8. grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)) 在容器变宽时会怎样?

单选题 · hard

  • A.固定为三列
  • B.自动增多或减少← 正确答案
  • C.永远只有一列
  • D.列宽始终保持 200px

答案:B。auto-fit 会让浏览器根据可用宽度自动计算列数,minmax(200px, 1fr) 给每列设了下限和弹性上限。容器越宽,能放下的列就越多,列宽也会随之变大。

Q9. 关于 Grid 与 Flexbox 的分工,正确的是?

多选题 · hard

  • A.Grid 适合二维骨架← 正确答案
  • B.Flex 适合一行排列← 正确答案
  • C.外层 Grid 内层 Flex← 正确答案
  • D.两者不能同时出现

答案:ABC。Grid 同时控制行和列,适合搭整页骨架;Flexbox 擅长一条线上的对齐,适合导航栏、按钮组。实际项目中“外层 Grid 拼区域,内层 Flex 排细节”是最常见的组合。

Q10. 网格项目放进了尚未显式定义的轨道时,自动创建的轨道叫?

单选题 · extreme

  • A.隐式轨道← 正确答案
  • B.显式轨道
  • C.单元格轨道
  • D.溢出轨道

答案:A。当项目超过 grid-template-columns/rows 定义的范围时,Grid 会自动生成额外轨道,也就是“隐式轨道”。它们像临时扩展出来的房间,尺寸由 grid-auto-columns 和 grid-auto-rows 控制。