ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

用 CSS Grid 重新思考布局:从嵌套 Div 到二维布局抽象

用 CSS Grid 重新思考布局:从嵌套 Div 到二维布局抽象 文档技术博客教程【免费下载链接】weekly前端精读周刊。帮你理解最前沿、实用的技术。项目地址https://gitcode.com/GitHub_Trending/we/weekly点击查看免费下载本文基于前端精读周刊第 124 期前沿技术/124.精读《用 css grid 重新思考布局》.md展开。CSS Grid 不止是一种新的排版手段更是一次布局思维范式的升级它将布局控制从层层嵌套的 Div 中解放出来收拢到父容器的一纸二维描述里。读完本文你将掌握grid-template-areas平铺式布局的完整写法与响应式重组技巧理解布局与 DOM 分离对组件模块化组织的深远影响并能在实际项目中正确地在 Grid 与 Flex 之间做技术选型。引言从一维到二维的布局跃迁Flex 与 Grid 相比就像功能键盘和触摸屏。功能键盘只能上下左右地控制光标x、y 轴而触摸屏打破了布局障碍直接以二维平面z 轴触达目标——无论 UI 内部布局再复杂都可以通过直接定位来描述。Flex 是一维布局方式为了形成复杂结构我们不得不不断嵌套 Div而一旦布局发生变更原有嵌套结构如果不能兼容变化到新结构代码就需要重构。Grid 则像触摸屏一样支持二维布局即便布局方式做了翻天覆地的调整也仅需少量修改即可适配。这正是本期精读要解决的问题理解这个革命性布局技术给布局组织甚至代码逻辑组织方式带来的变化。一维布局的通病Div 层级复杂与定制能力弱原文作者首先抛出 Flex 的问题这其实是block、float、flex三种布局模式的通病布局结构由 Div 层级结构描述导致 Div 层级复杂且遇到结构变更时难以维护定制能力弱。Flex 布局有一些不受控制的智能设定比如宽度 50% 的子元素会被同级元素挤到 50% 以下。这种智能化在某些场景是需要的但由于没有提供像 Grid 的minmax之类的 API定制型不足。以一张个人名片卡片为例用 Flex 描述它的 DOM 结构时HTML 的嵌套层级直接决定了视觉分区div classcard div classprofile-sidebar img srchttps://i.pravatar.cc/125?image3 alt classprofile-img / ul classsocial-list li a href# classsocial-link i classfab fa-dribbble-square/i /a /li li a href# classsocial-link i classfab fa-facebook-square/i /a /li li a href# classsocial-link i classfab fa-twitter-square/i /a /li /ul /div div classprofile-body h2 classprofile-nameRamsey Harper/h2 p classprofile-positionGraphic Designer/p p classprofile-info Lorem ipsum dolor sit amet consectetur adipisicing elit. Facere a tempore, dignissimos odit accusantium repellat quidem, sit molestias dolorum placeat quas debitis ipsum esse rerum? /p /div /div利用 HTML 嵌套结构我们把图形纵向分成两大块profile-sidebar与profile-body然后在每块内部继续嵌套划分布局——这是最经典的布局行为。对应的样式文件需要对每一层布局进行描述同时支持多分辨率弹性布局包括顶层card容器在内的一些样式都需要做调整.card { width: 80%; margin: 0 auto; display: flex; flex-direction: column; max-width: 600px; background: #005e9b; flex-basis: 250px; color: white; padding: 2em; text-align: center; } .profile-info { font-weight: 300; opacity: 0.7; } .profile-sidebar { margin-right: 2em; text-align: center; } .profile-name { letter-spacing: 1px; font-size: 2rem; margin: 0.75em 0 0; line-height: 1; } .profile-name::after { content: ; display: block; width: 2em; height: 1px; background: #5bcbf0; margin: 0.5em auto 0.65em; opacity: 0.25; } .profile-position { text-transform: uppercase; font-size: 0.875rem; letter-spacing: 3px; margin: 0 0 2em; line-height: 1; color: #5bcbf0; } .profile-img { max-width: 100%; border-radius: 50%; border: 2px solid white; } .social-list { list-style: none; justify-content: space-evenly; display: flex; min-width: 125px; max-width: 175px; margin: 0 auto; padding: 0; } .social-link { color: #5bcbf0; opacity: 0.5; } .social-link:hover, .social-link:focus { opacity: 1; } .bio { padding: 2em; display: flex; flex-direction: column; justify-content: center; } media (min-width: 450px) { .bio { text-align: left; max-width: 350px; } } .bio-title { color: #0090d1; font-size: 1.25rem; letter-spacing: 1px; text-transform: uppercase; line-height: 1; margin: 0; } .bio-body { color: #555; } .profile { display: flex; align-items: flex-start; } media (min-width: 450px) { .card { flex-direction: row; text-align: left; } .profile-name::after { margin-left: 0; } }可以看到一维布局把布局结构写死在 DOM 层级里视觉上左边是头像社交区、右边是文字区这种二维关系要靠profile-sidebarprofile-body两个容器才能表达要切换横竖排布还必须在media里同时修改多个容器的多项属性card的flex-direction、.profile-name::after的margin-left等才能联动生效。Grid 的核心 API用 grid-template-areas 平铺描述布局Grid 有许多 API原文重点聚焦grid-template-areas这个属性。利用它我们可以不关心模块的 HTML 结构直接以平铺方式描述布局。先看平铺后的 HTML——所有区块都是card的直接子元素不再有sidebar/body之类的分组容器div classcard img srchttps://i.pravatar.cc/125?image3 alt classprofile-img / ul classsocial-list li a href# classsocial-linki classfab fa-dribbble-square/i/a /li li a href# classsocial-linki classfab fa-facebook-square/i/a /li li a href# classsocial-linki classfab fa-twitter-square/i/a /li /ul h2 classprofile-nameRamsey Harper/h2 p classprofile-positionGraphic Designer/p p classprofile-info Lorem ipsum dolor sit amet consectetur adipisicing elit. Facere a tempore, dignissimos odit accusantium repellat quidem, sit molestias dolorum placeat quas debitis ipsum esse rerum? /p /div可以看到使用 Grid 可以将 UI 结构与 HTML 结构分离HTML 结构仅描述包含关系我们只需在样式文件中描述具体 UI 结构。样式文件只截取 Grid 相关部分.card { width: 80%; margin: 0 auto; display: flex; flex-direction: column; max-width: 600px; background: #005e9b; flex-basis: 250px; color: white; padding: 2em; text-align: left; display: grid; grid-template-columns: 1fr 3fr; grid-column-gap: 2em; grid-template-areas: image name image position social description; } .profile-name { grid-area: name; } .profile-position { grid-area: position; } .profile-info { grid-area: description; } .profile-img { grid-area: image; } .social-list { grid-area: social; }这段代码里几个关键点值得展开grid-template-columns: 1fr 3fr定义两列比例为 1:3。fr是 Grid 的弹性尺寸单位按比例瓜分剩余空间效果类似 Flex 中的伸缩因子但语义更可控grid-column-gap: 2em现代等价写法为column-gap: 2em定义列间距配合grid-row-gaprow-gap可以精确控制网格沟槽grid-template-areas用 3 行 × 2 列的字符串矩阵描述布局每个单元格中的标识符如image、name通过子元素的grid-area: xxx与之绑定同一标识符可以横跨多个单元格image同时占据第一列的第一、二行因此头像区域自动纵向贯通两行形成左侧一列、右侧上中下三块的二维格局。grid-template-areas是进一步抽象的语法它将页面结构通过直观的文本描述出来无论是理解还是修改都更为轻松。响应式适配只需重组模板字符串这种描述方式在适配不同分辨率时也具有天然优势——只要重组grid-template-areas即可media (min-width: 600px) { .card { text-align: left; grid-template-columns: 1fr 3fr; grid-template-areas: image name image position social description; } }对比前面的 Flex 版本Flex 需要在媒体查询里同时调整card的flex-direction、子容器的对齐方式等多项样式而 Grid 版本只需要重新声明grid-template-areas的矩阵结构子元素会自动跟随grid-area名称重新就位。归根结底Grid 通过二维结构描述把子元素的布局控制收到了父级使布局描述更加直观。选型结论Grid 与 Flex 各司其职原文作者也明确指出Flex 依然有使用场景简单的一维结构或者space-between等 Flex 独有语法的情况。因此推荐整体、复杂的二维布局采用 Grid一维的简单布局采用 Flex。抽象再抽象布局描述从 DOM 到字符串再到可视化Grid 的布局思路给了我们很多启发HTML 结构与 UI 结构的分离有助于减少 Div 的层级使代码看上去更清晰。也许有人会疑惑Grid 无非将 HTML 布局部分的功能挪到了 CSS整体复杂度应该不变。其实从grid-template-areas这个 API 可以看到Grid 不仅仅将布局功能抽到 CSS 中更是将布局描述进行了一层抽象使代码更易维护。为什么 Grid 可以对布局进行抽象因为 Grid 将二维结构都掌握在手中得到了更大的布局能力才能进一步把结构化的语法抽象为字符串描述。抽象的好处是不言而喻的——一堆嵌套的 Div 与下面这段代码哪个更易读呢.card { grid-template-areas: image name image position social description; }一般来说代码抽象程度越高就越易读、越易维护。可视化是对文本的再抽象同时规避非法布局在文本抽象之上还可以借助可视化工具把 Grid 渲染出来并以 UI 方式直接拖拽调整。UI 是对文本的再抽象同时可以规避一些不可能存在的语法比如下面这种非法描述.card { grid-template-areas: image name image position social image; }布局只能以凸多边形方式拓展一个区域可以纵向合并、横向合并、形成矩形或 L 形但不可能分离成两块也不可能突然插入其他模块而变成凹多边形。因此可视化工具可以将这类错误规避并简化为横竖多条线的方式对 UI 进行划分——这种描述方式效率更高。Grid 以及图形化插件的探索是布局领域的一大进步是不断抽象的尝试要解决的问题只有一个如何提供一种更直观的描述 UI 的方式。布局对模块化的影响让 JS 模块从 UI 视角回归逻辑视角Grid 将布局方式提高了一个维度会直接影响到 JS 的模块化方式。尤其是以 JSX 组织代码的情况下一个模块等于 UI JS通过嵌套方式的布局会让我们更倾向于站在 UI 视角划分模块。举个例子假设页面上有 AE 共 5 个内容模块它们的二维排布关系如下左侧一列A 在上、B 在下右侧两列C 横跨顶部D、E 在下方左右并排。如果用 Flex 方式布局我们可能会首先创建模块 X 作为左侧容器子元素是 A 和 B创建模块 Y 作为右侧容器子元素是 C 以及新容器 ZZ 容器的子元素是 D 和 E。如果你的第一印象是这么组织代码不得不承认模块化会受到布局方式的影响。虽然许多时候这样划分是正确的但当这 5 个模块各自没有关联时我们创建的容器 X、Y、Z 就失去了复用性在新的组合场景里又得重新组合一遍。但在 Grid 语法中我们不需要 X、Y、Z 这三层容器只需要用可视化生成器按照布局拖拽即可自动生成如下代码.parent { display: grid; grid-template-columns: 3fr repeat(2, 1fr); grid-template-rows: repeat(5, 1fr); grid-column-gap: 0px; grid-row-gap: 0px; } .div1 { grid-area: 1 / 1 / 3 / 2; } .div2 { grid-area: 3 / 1 / 6 / 2; } .div3 { grid-area: 1 / 2 / 2 / 4; } .div4 { grid-area: 2 / 2 / 6 / 3; } .div5 { grid-area: 2 / 3 / 6 / 4; }这里grid-area: 1 / 1 / 3 / 2是行起 / 列起 / 行止 / 列止的简写定位语法也可以写成grid-row: 1 / 3; grid-column: 1 / 2更直观配合repeat(2, 1fr)等模板语法5 个模块被精确地钉在网格坐标系上。其实grid-template-columnsgrid-template-rows组合起来使用比grid-template-areas更强大但纯代码方式描述没有grid-template-areas直观而一旦配合可视化系统这种按坐标钉格子的方式就非常直观了。将 AE 这 5 个模块的布局关系抽出来后它们之间的关系就打平了我们可以完全从逻辑视角审视如何做模块化——模块之间是否有关联、是否需要复用都由业务语义决定而不是由视觉嵌套决定。仓库印证布局抽象在周刊知识体系中的延伸这种布局与 DOM 分离的思路在本仓库其他精读中有着清晰的呼应可以作为进一步阅读的路径磁贴布局三部曲功能实现、性能优化磁贴布局以格子坐标系管理组件位置本质上是把 Grid 的二维网格思想工程化——组件只关心自己落在哪个行列区间容器只负责描述网格结构这正是grid-template-areas/grid-area定位哲学的实践延伸keepAlive 模式文中明确提到自由、磁贴、流式布局都可以通过拖拽轻松改变组件父元素而 keepAlive 方案通过createPortal实现DOM 结构与 React 实例结构分离——这与布局结构与 DOM 结构分离是同一思维在不同层面的复现且文末建议尽量采用绝对定位的布局策略来规避新增 DOM 嵌套带来的定位问题也印证了二维/绝对定位式布局在复杂场景下的优势State of CSS 2022中介绍的subgrid能力解决 grid 嵌套 grid 时子网格轨迹线无法对齐父网格的问题只需在子网格声明grid-template-columns: subgrid; grid-template-rows: subgrid;即可让子网格继承父网格的轨道定义——这是对网格抽象的进一步深化让嵌套场景也能保持坐标系的一致性。这些内容从不同侧面证明了同一结论把布局从内容结构中剥离、用更高维度的抽象来描述是前端布局发展的主旋律。总结布局与 DOM 分离的前端梦想CSS Grid 本质上是一种二维布局的语法相比 Block、Flex 等一维布局方案多了一个维度可以同时从行与列角度定义布局因此派生出grid-template-areas等语法整体上更内聚、更直观抽象度也更高了。理解了这些也就理解了布局未来的发展方向让布局与 DOM 分离一直是前端的一个梦想。开发 UI 部分时只需关心页面由哪些模块组成、去实现这些模块就行了而不需要关心模块之间应该如何组合。在描述组合时可以通过可视化或比较抽象的字符串描述布局的结构并对应到写好的模块上——这样的代码维护性远高于用 Div 描述结构的方案。实际项目中可以按如下准则落地场景推荐方案理由整体页面骨架、复杂二维排布Gridgrid-template-areas布局描述收拢到父级文本直观响应式只需重组模板字符串局部一维排列导航、按钮组、图标列表Flex简单直接且space-between、space-evenly等语法是 Flex 独有组件间无关联的复杂组合Grid 可视化生成器消除 X/Y/Z 式冗余容器模块关系打平回归逻辑视角划分模块网格嵌套需对齐轨道Grid subgrid子网格继承父网格轨道保持坐标系一致详见 State of CSS 2022本文依据仓库文档 前沿技术/124.精读《用 css grid 重新思考布局》.md 编写文中所有代码示例均继承自原文并补充了参数语义说明延伸阅读路径见本仓库 readme.md 的周刊目录。赞分享文档技术博客教程【免费下载链接】weekly前端精读周刊。帮你理解最前沿、实用的技术。项目地址https://gitcode.com/GitHub_Trending/we/weekly点击查看免费下载相关推荐终极CSS Grid嵌套布局指南从零掌握复杂组件设计的完整攻略终极CSS Grid嵌套布局指南从零掌握复杂组件设计的完整攻略 CSS Grid布局是现代前端开发中构建复杂界面的强大工具尤其在处理嵌套结构时展现出卓越的灵教程示例工程使用 CSS Grid 实现二维布局Front-End-Checklist 规则实战指南使用 CSS Grid 实现二维布局Front End Checklist 规则实战指南 CSS Grid 是 CSS 中最强大的二维布局系统它专门解决同freeCodeCamp 课程源码解析用嵌套 CSS Grid 构建「网格中网格」布局freeCodeCamp 课程源码解析用嵌套 CSS Grid 构建「网格中网格」布局 本篇以 freeCodeCamp 响应式设计课程中「Create Gr前端后端教育上一篇wechatpay-apache-httpclient高级定制打造符合企业需求的支付请求处理器下一篇Keyshade完全指南如何构建实时秘密与配置管理系统创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表