CSS栅格系统与弹性盒模型:实践比较

多年以前,所有的HTML页面布局都是通过table、float以及其他CSS属性完成的,但这些方法并不适合构建复杂的web页面。

于是,W3C推出了flexbox(弹性盒模型)——一种专门用于构建健壮的响应式页面的布局模式。使用flexbox可以很容易地对齐页面元素和内容,而它也是现在大多数web开发者首选的CSS系统。

现在,最佳HTML布局系统的称号又多了一个新的竞争者,它就是强大的CSS Grid,3月底,它就已经得到了Firefox 52和Chrome 57的原生支持,其他浏览器相信也会紧随其后。

一个基本布局测试

为了了解两种系统的布局方式,我们会分别用这两种系统构建同一个页面,一次使用flexbox(弹性盒模型),一次使用CSS Grid(栅格系统)。页面如下:

静态页面布局
(一个精简的静态页面布局)

这种设计非常基础——包含一个居中容器,其中放置页头、主体部分、侧边栏和页脚。为了完成这个布局,我们需要解决这几个问题:

  1. 对齐布局中的四个部分(页头、主体、侧边栏、页脚)。
  2. 响应式设计(在小屏幕上侧边栏置于主体下方)。
  3. 对齐页头内容——导航置于左侧,按钮置于右侧。

为了便于比较,我们一切从简,先从第一个问题开始。

挑战一:页面部分定位

Flexbox解决方案

我们首先使用flexbox解决这个问题,为容器添加display:flex,并且垂直定位子元素,也即让每个部分纵向排列。

.container {
    display: flex;
    flex-direction: column;
}

现在我们来让主体部分和侧边栏靠在一起,由于我们已经设置其父容器为纵向,所以我们需要添加一个包装元素,来重新设置主体与侧边栏的定位方式。

<header></header>
<div class="main-and-sidebar-wrapper">
    <section class="main"></section>
    <aside class="sidebar"></aside>
</div>
<footer></footer>

然后我们将包装元素设置为flex容器(display:flex)并将定位方式设置为横向(flex-direction:row)。

.main-and-sidebar-wrapper {
    display: flex;
    flex-direction: row;
}

最后一步是设置主体与侧边栏的尺寸,我们将其尺寸设置为3:1,使用flex很容易完成这个目标。

.main {
    flex: 3;
    margin-right: 60px;
}
.sidebar {
   flex: 1;
}

我们可以看到,flexbox完美地完成了这项工作,但是我们仍然需要添加大量的CSS属性和一个额外的HTML元素。接下来让我们看看CSS Grid是如何解决这个问题的。

CSS Grid解决方案

CSS Grid有多种使用方法,此处我们使用“栅格模板区域(grid-template-areas)”语法,因为它看起来最适合完成这个任务。
首先来定义四个栅格区域,每个对应页面的一个部分。

<header></header>
<!-- 注意此处无包装元素 -->
<section class="main"></section>
<aside class="sidebar"></aside>
<footer></footer>
header {
    grid-area: header;
}
.main {
    grid-area: main;
}
.sidebar {
    grid-area: sidebar;
}
footer {
    grid-area: footer;
}

然后设置网格并且分配每个区域的位置。下面的代码可能乍一看很复杂,但一旦你了解了栅格系统它就会变得极易掌握。

.container {
    display: grid;

    /*在网格中定义列的数量和大小。
      单位fr工作原理与flex类似:
      fr列元素将按照其值共享同一行中的可用空间。
      下面的代码表示有两列,第一列是第二列的三倍。*/
    grid-template-columns: 3fr 1fr;

    /*分配前面定义的网格区域。
      第一行均为header。
      第二行由main和sidebar共享。
      第三行均为footer。*/
    grid-template-areas: 
        "header header"
        "main sidebar"
        "footer footer";

    /*每个网格间距60px*/
    grid-gap: 60px;
}

这样就完成了要求,布局将遵从上述结构,并且使用这种方式我们甚至不用处理内边距和外边距。

挑战二:响应式设计

Flexbox解决方案

这一步与前一步紧密联系在一起,对于flexbox来说我们必须改变包装元素的flex-direction并且调整边距。

@media (max-width: 600px) {
    .main-and-sidebar-wrapper {
        flex-direction: column;
    }

    .main {
        margin-right: 0;
        margin-bottom: 60px;
    }
}

由于我们的示例很简单,所以此处需要做的工作并不多,但在一个更复杂的布局中,就会有许许多多的内容需要重定义了。

CSS Grid解决方案

由于我们已经定义好了grid-areas,所以我们只需要在媒体查询中对网格进行重排序即可。

@media (max-width: 600px) {
    .container {
    /* 在移动设备上重排序网格*/
        grid-template-areas: 
            "header header"
            "main main"
            "sidebar sidebar"
            "footer footer";
    }
}

或者,我们也可以重新定义整个布局,如果你觉得这样可以更简洁些。

@media (max-width: 600px) {
    .container {
        /*  将页面重新定义为单列布局 */
        grid-template-columns: 1fr;
        grid-template-areas: 
            "header"
            "main"
            "sidebar"
            "footer";
    }
}

挑战三:对齐页头组件

Flexbox解决方案

我们的页头中包括一些导航链接和一个按钮。我们希望将导航置左,按钮置右。导航中的链接必须相邻对齐。

<header>
    <nav>
        <li><a href="#"><h1>Logo</h1></a></li>
        <li><a href="#">Link</a></li>
        <li><a href="#">Link</a></li>
    </nav>
    <button>Button</button>
</header>

设置CSS样式:

header {
    display: flex;
    /*平铺页头内容*/
    justify-content: space-between;
}

现在导航列表和按钮已经正确对齐了,剩下的任务就是使<nav>中的元素水平移动了,最简单的方法是使用display:inline-block,但是为了完全使用flexbox来完成这个任务,我们使用下面这个解决方案:

header nav {
    display: flex;
    /*项目位于容器的基线上*/
    align-items: baseline;
}

仅有两行代码,还不错。接下来我们看看CSS Grid是如何解决的。

CSS Grid解决方案

为了分离导航和按钮,我们将<header>设置为2列栅格,另外我们需要额外两行CSS定位它们各自的边界。

header{
    display: grid;
    grid-template-columns: 1fr 1fr;
}
header nav {
    justify-self: start;
}
header button {
    justify-self: end;
}

但此时<nav>中的嵌入式链接,尚未正确对齐,因为CSS Grid中没有类似flexbox的baseline(基线)选项。所以我们必须再定义一个子网格。

header nav {
    display: grid;
    grid-template-columns: auto 1fr 1fr;
    align-items: end; 
}

很明显CSS Grid在完成这项布局中存在一些问题,但也是意料之中的事——毕竟它主要是用来对齐容器的,而不是容器中的内容,它并不适合做最后的润色。

总结

如果你通读了全文,结论就很显然了——并不存在一个最好的布局系统,flexbox和CSS Grid有各自擅长的领域,完全可以一起使用,而非一个能够替代另一个。

对于没时间通读全文的读者,可以看一看最后的总结:

  • CSS Grid更适用于页面的整体架构,非常易于管理页面布局,甚至可以用于非常规、不对成设计中。
  • Flexbox擅长进行元素中内容的对齐,可以使用flex定位设计中的细节部分。
  • 使用CSS Grid进行2D布局(行和列)。
  • Flexbox更适合一维空间(仅行或列)。
  • CSS Grid和flexbox不是替代关系,两者完全可以搭配使用。

转载请注明以上内容。