1. 引言
Flex 与 Grid 相比就像功能键盘和触摸屏。触摸屏的控制力相比功能键盘来说就像是降维打击,因为功能键盘只能上下左右控制(x、y 轴),而触摸屏打破了布局障碍,直接从(z 轴)触达,这样 无论 UI 内部布局再复杂,都可以通过 touch 直接定位。
Flex 是一维布局方式,我们需要不断嵌套 Div 才能形成复杂结构,而一旦布局产生了变化,原有嵌套结构如果不能 “兼容变化” 到新结构,代码就需要重构。而 Grid 就像触摸屏一样,可以二维布局,即便布局方式做了翻天覆地的调整,也仅需少量修改就能适配。
也就是这次用 css grid 重新思考布局 的原因,理解这个革命性布局技术给布局,甚至代码逻辑组织带来的变化。
2. 概述
Flex 布局的问题,其实是 block
float
flex
这三种布局模式的通病:
- 布局结构由 Div 层级结构描述,导致 Div 层级复杂且遇到结构变更时难以维护。
- 定制能力弱。Flex 布局有一些不受控制的智能设定,比如宽度 50% 的子元素会被同级元素挤到 50% 以下,这种智能化在某些场景是需要的,但由于没有提供像 Grid 的
minmax
之类的 API,所以定制型不足。
思考一个问题
面对这样的布局,我们一般会怎么做?
html结构
<div class="profile flex"> <div class="profile-slidebar"> <img class="profile-avatar" src="https://stage-image.szsing.com/avatar/2020-05-23/BN45pcC5kd_avatar_10002628_large_142148_0611799308.png" alt=""> <ul class="profile-list"> <li><a href="#" class="profile-link"><i class="bkact-icon-qq"></i></a></li> <li><a href="#" class="profile-link"><i class="bkact-icon-weibo"></i></a></li> <li><a href="#" class="profile-link"><i class="bkact-icon-wechat"></i></a></li> </ul> </div> <div class="profile-body"> <h2 class="profile-name">Ramsey Harper</h2> <p class="profile-position">Graphic Designer</p> <p class="profile-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 嵌套结构,我们将图形纵向分成两大块,然后在每块内部继续嵌套划分布局,这是最经典的布局行为了。
css样式(只涉及布局样式)
.flex.profile { display: flex;}.flex .profile-sidebar { margin-right: 2em; text-align: center;}.flex .profile-body { padding: 2em; display: flex; flex-direction: column; justify-content: center; text-align: left; max-width: 350px;}
看看 Grid 是怎么做的吧?Grid 有许多 API,我们重点看 grid-template-areas
这个属性,利用它,我们可以不关心模块的 HTML 结构,直接平铺方式描述:
<div class="profile grid"> <img class="profile-avatar" src="https://stage-image.szsing.com/avatar/2020-05-23/BN45pcC5kd_avatar_10002628_large_142148_0611799308.png" alt=""> <ul class="profile-list"> <li><a href="#" class="profile-link"><i class="bkact-icon-qq"></i></a></li> <li><a href="#" class="profile-link"><i class="bkact-icon-weibo"></i></a></li> <li><a href="#" class="profile-link"><i class="bkact-icon-wechat"></i></a></li> </ul> <h2 class="profile-name">Ramsey Harper</h2> <p class="profile-position">Graphic Designer</p> <p class="profile-info"> Lorem ipsum dolor sit amet consectetur adipisicing elit. Facere a tempore, ignissimos odit accusantium repellat quidem, sit molestias dolorum placeat quas debitis ipsum esse rerum? </p></div>
可以看到,使用 Grid 可以将 UI 结构与 HTML 结构分离,HTML 结构仅描述包含关系,我们只需在样式文件中描述具体 UI 结构。
css样式(只涉及布局样式)
.grid.profile { display: grid; grid-template-columns: 1fr 3fr; grid-column-gap: 2em; grid-template-areas: "avatar name" "avatar position" "social description";}.grid .profile-name { grid-area: name;}.grid .profile-position { grid-area: position;}.grid .profile-info { grid-area: description;}.grid .profile-avatar { grid-area: avatar;}.profile-list { grid-area: social;}
可以看到,grid-template-areas
是进一步抽象的语法,将页面结构通过直观的文本描述,无论是理解还是修改都更为轻松。
这种描述方式适配不同分辨率下重新排布结构和位置也具有优势,只要重组 grid-template-areas
即可:
@media (max-width: 600px) { .grid.profile { text-align: left; grid-template-columns: 1fr; grid-template-areas: "avatar" "social" "name" "position" "description" } .grid .profile-list { margin-left: 0; margin-top: 10px; }}
归根结底,Grid 通过二维结构描述,将子元素布局控制收到了父级,使布局描述更加直观。
3. 思考
Grid 的布局思路给了我很多启发,HTML 结构与 UI 结构的分离有助于减少 DIV 的层级结构,使代码看上去更清晰。
也许有人会疑惑,Grid 无非将 HTML 布局部分功能挪到了 CSS,整体复杂度应该不变。其实,从 grid-template-areas
这个 API 可以看到,Grid 不仅仅将布局功能抽到 CSS 中,更是将布局描述进行了一层抽象,使代码更易维护。
抽象,再抽象
为什么 Grid 可以对布局进行抽象?因为 Grid 将二维结构都掌握在手中,得到了更大的布局能力,才能进一步将结构化语法抽象为字符串的描述。
抽象的好处是不言而喻的,你觉得一堆嵌套的 DIV 与下面的代码,哪个更易读呢?
.profile {
grid-template-areas:
"image name"
"image position"
"social description";
}
这就是抽象的好处,一般来说,代码抽象程度越高就越易读,越易维护。
4.布局对模块化的影响
Grid 将布局方式提高了一个维度,会直接影响到 JS 模块化方式。
尤其是以 JSX 组织代码的情况下,一个模块等于 UI + JS,通过嵌套方式的布局会让我们更倾向于站在 UI 视角划分模块。
比如对于上图模块,如果用 Flex 方式布局,我们可能会首先创建模块 X 作为左侧容器,子元素是 A 和 B,创建模块 Y 作为右侧容器,子元素是 C 以及新容器 Z,Z 容器的子元素是 D 和 E。
<div> <X> <A></A> <B></B> </X> <Y> <C></C> <Z> <D></D> <E></E> </Z> </Y></div>
虽然许多时候这样划分是正确的,但当这 5 个模块各自没有关联时,我们创建的容器 X、Y、Z 就失去了复用性,在新的组合场景我们又要重新组合一遍。
但是在 Grid 语法中,我们不需要 X、Y、Z,只需要用 css grid generator 按照上图的方式拖拖拽拽即可自动生成如下布局代码:
div {
display: grid;
grid-template-columns: 3fr repeat(2, 1fr);
grid-template-rows: repeat(5, 1fr);
grid-column-gap: 0px;
grid-row-gap: 0px;
}
A {
grid-area: 1 / 1 / 3 / 2;
}
B {
grid-area: 3 / 1 / 6 / 2;
}
C {
grid-area: 1 / 2 / 2 / 4;
}
D {
grid-area: 2 / 2 / 6 / 3;
}
E {
grid-area: 2 / 3 / 6 / 4;
}
其实 grid-template-columns
grid-template-rows
组合起来使用比 grid-template-areas
更强大,但是纯代码方式描述没有 grid-template-areas
直观,可是配合一些可视化系统就非常直观了:
将 A ~ E 这 5 个模块布局抽出来后,它们之间的关系就打平了,我们可以完全从逻辑视角审视如何做模块化了。
5. 总结
CSS Grid 本质上是一种二维布局的语法,相比 Block、Flex 等一维布局方案,多了一个维度可以同时从行与列角度定义布局,因此派生出 grid-template-areas
等语法,整体上更内聚更直观,抽象度也更高了。
理解了这些也就理解了布局未来的发展方向,让布局与 Dom 分离 一直是前端的一个梦想,开发 UI 部分时,只需关心页面由哪些模块组成,去实现这些模块就行了,而不需要关心模块之间应该如何组合。在描述组合时,可以通过可视化或比较抽象的字符串描述布局的结构,并对应到写好的模块上,这样的代码维护性远高于用 DIV 描述结构的方案。
转自:dt-fe/weekly
原文链接: 精读《用CSS Grid 重新思考布局》