惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

AI
AI
TaoSecurity Blog
TaoSecurity Blog
H
Heimdal Security Blog
Help Net Security
Help Net Security
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Microsoft Azure Blog
Microsoft Azure Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Google DeepMind News
Google DeepMind News
爱范儿
爱范儿
The Cloudflare Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
人人都是产品经理
人人都是产品经理
大猫的无限游戏
大猫的无限游戏
N
News | PayPal Newsroom
V2EX - 技术
V2EX - 技术
博客园 - 【当耐特】
D
Darknet – Hacking Tools, Hacker News & Cyber Security
S
Secure Thoughts
C
CERT Recently Published Vulnerability Notes
罗磊的独立博客
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
P
Privacy & Cybersecurity Law Blog
有赞技术团队
有赞技术团队
S
Schneier on Security
S
SegmentFault 最新的问题
Google Online Security Blog
Google Online Security Blog
H
Hacker News: Front Page
The Last Watchdog
The Last Watchdog
Schneier on Security
Schneier on Security
PCI Perspectives
PCI Perspectives
IT之家
IT之家
Project Zero
Project Zero
博客园 - 司徒正美
P
Privacy International News Feed
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Jina AI
Jina AI
Security Latest
Security Latest
Hacker News - Newest:
Hacker News - Newest: "LLM"
腾讯CDC
C
CXSECURITY Database RSS Feed - CXSecurity.com
阮一峰的网络日志
阮一峰的网络日志
C
Check Point Blog
aimingoo的专栏
aimingoo的专栏
V
Vulnerabilities – Threatpost
W
WeLiveSecurity
NISL@THU
NISL@THU
Webroot Blog
Webroot Blog
N
Netflix TechBlog - Medium
L
Lohrmann on Cybersecurity

講評世界

My App Defaults 2023 读书的意义 そして、次の曲が始まるのです 从零开始的 RSSHub Docker 私有化部署指南 使用 Homebrew 安装 Typora 的 0.11.18 版本 使用 TypeScript 为 Vue 组件的 prop 标注类型 【译文】IndexedDB 为什么这么慢?如何更好的使用呢? Hello 2022 「他山之石」零贰 「他山之石」零壹 「言論」 零壹 给 icarus 主题增加所有文章的字数统计 hexo 无法在本地实时预览 JavaScript 立即调用的函数表达式(IIFE) 解决 nvm 无法在 arm 架构下安装 V15 以下的 node 版本 的问题 m1 芯片安装 nvm 提示 command not found 如何在 JavaScript 完美的确定一个数据的类型 Cookie?小饼干! 使用 RSS 在推荐算法中获取主动权
【译文】Grid 用于布局, Flexbox 用于组件
Moeyua · 2022-03-05 · via 講評世界

发布于 # 译文

本文是 Ahmad Shadeed 的博客文章 Grid for layout, Flexbox for components 的翻译。

3 月 5 号开始翻译,摸了 3 个月终于翻译完了,下次还敢(不是


我的弟弟是一名刚毕业的软件工程师,现在他正在做前端开发相关的实习岗位。他以前学过 Grid 和 flexbox,但是我发现和我经常网上看到的情况一样,他在布局的时候使用 Grid 还是 flexbox 之间摇摆不定。举个例子,他尝试使用 Grid 布局去开发一个网站的 header,但是当他使用了 grid-column 属性的时候他发现过程好像并不像想象中那么顺利,所以他只能不停地调整来让页面看起来和设计稿一致。

说句实话,我不太喜欢这样子,所以我试着去找一些关于这方面的资料来让他了解 grid 和 flexbox 之间的区别,最好还能带上几个例子,但可惜的是我一无所获。所以我尝试写了一篇涵盖了这个主题所有内容的文章,希望大家能够从中获益。

介绍

在深入探讨概念和实例之前,首先要确保你了解 CSS grid 和 flexbox 的主要区别。CSS Grid是一个拥有「行」和「列」概念的多维布局模块。Flexbox 可以将其子项布局成列或行,但不能同时进行(译者注:可以理解为一维)。

如果你还不了解关于 CSS grid 和 flexbox 相关的知识,我非常建议你去阅读这篇 可视化文章。如果你已经了解了这些内容那就太棒了,接下来我们就将深入了解它们直接的区别,以及如何使用它们。

Grid 和 Flexbox 之间的不同点

首先我需要澄清一点,关于「何时使用 CSS Grid 或 flexbox」这里没有一个非常明确的界线。还有一点就是,没有「使用这种方法 正确 或者 错误」这种说法。这篇文章是推荐在特定的使用情况下使用某种技术的指南,我将会讲述一些基本概念,然后通过一些例子说明这些概念,剩余的就需要靠你自己去探索和实验了。

/* Flexbox 容器 */
.wrapper {
  display: flex;
}

/* Grid 容器 */
.wrapper {
  display: grid;
  grid-template-columns: 2fr 1fr;
  grid-gap: 16px;
}

grid-vs-flexbox

发现了什么吗?Flexbox 是在一行内布局自己的元素,CSS grid 使其转化为拥有行和列的表格。Flexbox 是在行内进行对齐的,当然如果我们愿意也可以在列内。

/* Flexbox 容器 */
.wrapper {
  display: flex;
  flex-direction: column;
}

grid-vs-flexbox-1

如何决定使用哪种布局呢

在 CSS grid 和 flexbox 之间做决定有时会很困难,特别是刚入门 CSS 的新手,我非常理解这种心情,我在开始选择之前会问自己下面这几个问题:

  • 组件内的元素是如何展示的?行内还是行和列?

  • 组件在不同种类的屏幕大小下期望的展示方式是什么?

如果组件的子元素的排列方式是行内,那大多数情况最好的方案应该就是 flexbox 了。看看下面这个例子:

decide-1

如果是行和列的话,CSS grid 应该是这种情况下的方案。

decide-2

现在我已经说明了它们之间主要的不同点,现在让我们看一下更加特殊的例子来学习如何决定这两种布局。

一些案例和实例

在下面的章节中,我将详细讨论 flexbox 和 grid 的不同使用情况。

CSS Grid
两栏式布局(Main And Sidebar)

当你需要一个两栏式布局(一个侧边栏和主要内容)时,CSS grid 就是最好的选择,请看下面这个例子:

grid-use-1

这里是实现代码:

<div class="wrapper">
  <aside>Sidebar</aside>
  <main>Main</main>
</div>
@media (min-width: 800px) {
  .wrapper {
    display: grid;
    grid-template-columns: 200px 1fr;
    grid-gap: 16px;
  }
  aside {
    align-self: start;
  }
}

如果 <aside> 元素没有使用 align-self 属性,那么它的高度就会无视内容,始终等于 <main> 元素。

Cards Grid(网格卡片)

正如我们在文章开头所说的,布局 cards grid 的最佳方式从 CSS grid 的名字就不言而喻了。

grid-use-2

这里是我实现这个布局的代码:

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

每一列的宽度最小应该是 200px,如果没有足够的空间则会自动换到下一行。需要注意的是,如果视口(viewport)的宽度小于 200px ,上述写法会导致在水平方向上发生滚动。

一个比较简单的解决方式是只有在 viewport 的宽度足够时上述代码才会生效,像下面这样:

@media (min-width: 800px) {
  .wrapper {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    grid-gap: 16px;
  }
}
Section Layout

在下面的这个例子中,我们可以使用两次 grid 布局来实现。首先我们使用 grid 布局将整个区域分成两个部分(侧边栏部分和表单部分),接着我们就可以使用 grid 将表单进行布局。

grid-use-3

I can’t emphasis how CSS grid is perfect for that. 下面是 CSS 实现代码:

@media (min-width: 800px) {
  .wrapper {
    display: grid;
    grid-template-columns: 200px 1fr;
  }
  
  .form-wrapper {
    display: grid;
    grid-template-columns: 1fr 1fr;
    grid-gap: 16px;
  }
  
  .form-message,
  .form-button {
    grid-column: 1 /3; /* 让它们充满整个宽度 */
  }
}

这个例子引用自我在 Envato 的 这一篇文章,这是一篇关于在 web 开发时如何使用 CSS grid 进行布局的文章,非常值得一读。

CSS Flexbox
网站导航

在 90% 的情况下,网站的导航栏都应该使用 CSS flexbox 进行开发。它们最大的共同之处就是在左边有一个 logo,导航的部分都在右边。这种情况非常适用于 flexbox。

flexbox-use-1

要想实现上面这个例子,你只需要按照下面的方式进行设置:

.site-header {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
}

同样的,上述代码也可以用在下面的这种设计中。

flexbox-use-2

你应该注意到导航栏的结构发生了一点变化,但是子元素之间的间距仍由 ustify-content 属性决定。

操作列表

当你听到列表的时候第一反应一定是垂直排列的列表。这里特别说明一下,一个列表也可以在一行内排列,所以各位不要搞错。

关于操作列表的例子我们可以参考 Facebook 或者 Twitter。操作列表由几个操作按钮组成。我们看看下面的这张截图吧:

flexbox-use-3

就像你看到的那样,所有元素之间彼此相邻,而且它们水平分布。Flexbox 简直是最完美的选择,这也是它的核心用途之一。

.action-list {
  display: flex;
}

.action-list_item {
  flex: 1; /* 让所有子元素扩大,使得它们能够平分所有空间 */
}

另一个类似的用法就是用作弹窗的标题和操作按钮。 不管弹窗页脚 footer 还是页眉 header 的子元素全都是在行内排列,就像你看到的,他们之间的空隙如下所示: flexbox-use-4 对于弹窗的页眉来说,下面这种写法就能够满足需要:

.modal-header {
	display: flex;
	justify-content: space-between;
}

页脚对于我们来说可能有一些不同的地方。「取消」按钮需要将 margin 设置为 auto 来将它放置到右边。关于这一点我写了一篇详细介绍这方面内容的 [文章](https://ishadeed.com/article/auto-css/

.cancel_action {
	margin-left: auto;
}

.cancel_action 可能在这里不是一个好的命名方式,但是在这篇文章里我并不打算详细说明有关 CSS 的命名规则。

表单元素

一个输入框旁边紧挨着一个按钮的组合可以说是 Flexbox 的完美案例了。 请看下面这个例子:

flexbox-use-5

在第一个表单当中,输入框占据了所有的剩余空间,所以我们需要给它设置一个动态的宽度。同样的,第二个表单(Facebook 的 Messenger)也是一样,文字输入框占据了所有的剩余位置。让我们试着模仿一下。

flexbox-use-6

.input {
  flex: 1 1 auto;
}

主要注意的是,如果我们没有在文字输入框上设置 flex: 1 1 auto,那么输入框就不会自动填充满整个剩余空间。

跟帖回复

Flexbox 的另一个比较通用的例子就是「跟帖回复」,比如下面这个例子:

flexbox-use-7

这里有用户的头像和评论本身,评论模块占据了容器的所有剩余空间,用 flexbox 可以完美的实现这种布局。

卡片组件

卡片组件的种类有非常多,但是一般来说最常见的还是下面例子中的这种设计:

flexbox-use-8

左边的例子中,我们将 flex 的方向设置为了 column,所以卡片的子元素是叠在一起的。相反,右边的方向是 row ,而且 flexbox 默认的方向就是 row,这一点千万不要忘记。

.card {
	display: flex;
	flex-direction: column;
}
@media (min-width: 800px) {
	.card {
		flex-direction: row;
	}
}

另外一个比较常见的变种是在卡片中有一个图标,而且会有一个文字标签紧挨在下面。它可能是一个按钮、一个链接、或者就仅仅是装饰而已。让我们看看下面这个例子:

flexbox-use-9

值得注意的是图标和文本标签在水平和垂直方向是居中的。感谢 flexbox,让我们能够很简单的实现这种布局。

.card {
  display: flex;
  flex-direction: column;
  align-items: center;
}

行内的样式是默认的,我们只需要删除 flex-direction: column 让它恢复到默认的值 <row> 就可以了。

标签页 / 底部菜单

当一个元素的宽度需要始终等于屏幕宽度,而且它的子元素需要填满所有的可用空间,这时 flexbox 就是我们的最佳选择。

flexbox-use-10

在上面这个例子中,所有的子元素都应该充满可用空间,而且所有子元素的宽度相同。我们只需要将容器的 display 设置为 flex 就可以很轻松的实现这一布局了。

.tabs_item {
  flex-grow: 1;
}

这种方式被 React Native 框架用来在移动端创建 Tab 菜单。这里有一段 React Native 的示例代码展示了和上面一样的内容。这里的代码是从 这里 借鉴来的。

import React from "react";
import { View } from "react-native";

export default FlexDirectionBasics = () => {
  return (
    <View style=>
      <View
        style=
      />
      <View
        style=
      />
      <View
        style=
      />
    </View>
  );
};
功能列表

关于 flexbox 最喜欢的一点就是它能够随意翻转元素的方向。flexbox 默认的方向是 row ,但是我们可以像下面这样转换一下:

.item {
	flex-direction: row-reverse;
}

在下面这个例子中我们能看到偶数项被翻转,这就是通过上面的方式实现的,非常实用。

flexbox-use-11

居中内容

然后我们设想这样一种情况:我们有一个很重要的部分,这个部分的内容需要被垂直且水平居中。水平居中我们能够使用文本对齐简单的实现。

flexbox-use-12

.hero {
  text-align: center;
}

但是如何使用 flexbox 将元素垂直居中呢?这正是我们需要实现的:

.hero {
	display: flex;
	flex-direction: column;
	align-items: center; /* 水平居中元素 */
	justify-content: center; /* 垂直居中元素 */
	text-align: center;
}

CSS Grid 和 Flexbox 的结合

不仅每个布局模块有自己的使用例,我们甚至可以将他们结合起来使用。当我考虑如何将这两种布局方式结合在一起的时候,我脑海中的第一个想法就是卡片列表。使用 Grid 来布局卡片,使用 flexbox 来布局卡片组件自身。

grid-and-flex

上面的这个布局有以下几点需求:

  • 每一行的卡片高度应该保持相等;

  • 不管卡片高度如何,Read more 按钮应该始终在卡片的底部;

  • Grid 应当使用 minimal() 函数。

<div class="wrapper">
  <article class="card">
    <img src="sunrise.jpg" alt="" />
    <div class="card__content">
      <h2><!-- Title --></h2>
      <p><!-- Desc --></p>
      <p class="card_link"><a href="#">Read more</a></p>
    </div>
  </article>
</div>
@media (min-width: 500px) {
  .wrapper {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    grid-gap: 16px;
  }
}

.card {
  display: flex; /* [1] */
  flex-direction: column; /* [2] */
}

.card__content {
  flex-grow: 1; /* [3] */
  display: flex; /* [4] */
  flex-direction: column;
}

.card__link {
  margin-top: auto; /* [5] */
}

现在让我来解释一下上面的 CSS。

  1. 将卡片设置成 flexbox 容器
  2. 排列方向为纵向,也就是说卡片的元素是通过栈的方式进入的
  3. 让卡片的内容扩大并充满所有剩余空间
  4. 将卡片内容设置为 flexbox 容器
  5. 最后,使用 margin-top: auto 将 链接 推入栈中。这样不管卡片高度是多少它都会保持在最底部。

正如你所看到的,结合 CSS grid 和 flexbox 并不是很困难。这两种方式可以给我们带来许多种实现 web 布局的方式。我们应该正确的使用它们,而且只在像上述需要的情况去结合它们。

支持旧版本的浏览器的回退方案

通过 CSS @supports

大概在几个月之前,我得到了一条推特回复说我的网站在 IE11 上崩溃了。在检查过后我发现了一个奇怪的现象。所有的网站内容都被压缩都了左上角的区域。我的网站无法使用了!

ishadeed-ie11

是的,这就是我的网站——一个前端开发工程师的网站,在 IE11 上。首先让我感到困惑的是,这是如何发生的?我记得 CSS grid 是支持 IE11 的,但是这是微软发布的旧版本。解决方法非常简单,那就是使用 @supports 只在新的浏览器中使用 CSS grid。

@supports (grid-area: auto) {
  body {
    display: grid;
  }
}

让我解释一下。我使用 grid-area 是因为它只在新的 CSS grid 规范中被支持,从2017年3月到今天。由于IE不支持 @supports 查询,整个规则将被忽略。因此,新的CSS网格将只用于支持的浏览器。

使用 Flexbox 作为 CSS Grid 的回退方案

flexbox 不适合用于展示网格布局中的项目,但是这并不意味着它不能当作备用方案。你可以使用 flexbox 来作为 不支持 CSS grid 浏览器的备用方案。我曾经开发了一个工具l用来解决这个问题。

@mixin grid() {
  display: flex;
  flex-wrap: wrap;

  @supports (grid-area: auto) {
    display: grid;
    grid-gap: 16px 16px;
  }
}

@mixin gridAuto() {
  margin-left: -16px;

  > * {
    margin-bottom: 16px;
    margin-left: 16px;
  }

  @media (min-width: 320px) {
    > * {
      width: calc((99% / #{2}) - 16px);
      flex: 0 0 calc((99% / #{2}) - 16px);
    }
  }

  @media (min-width: 768px) {
    > * {
      width: calc((99% / #{3}) - 16px);
      flex: 0 0 calc((99% / #{3}) - 16px);
    }
  }

  @supports (grid-area: auto) {
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    margin-left: 0;

    > * {
      width: auto;
      margin-left: 0;
      margin-bottom: 0;
    }
  }
}

上面的回退代码通过下面所述的方式运行:

  1. 添加 display: flexflex-wrap: wrap 使元素换行;
  2. 检查 CSS grid 是否被支持,如果支持,那么就会使用 display: grid
  3. 通过使用 > *选择器,我们可以选择容器的直接子后代。在选择完成后,我们就可以给它们每一个都添加一个特殊的宽度或者大小了。
  4. 当然,它们之间的间隙是必须的,如果浏览器支持 CSS grid,我们将会用 grid-gap 来充当间隙。

这里是一个使用 Sass mixin 的例子:

.wrapper {
  @include grid();
  @include gridAuto();
}

Demo

如果 Grid 和 Flexbox 都无法正常使用

当我和我弟弟在进行代码评审的时候,我注意到了几个 CSS grid 和 flexbox 都会错误的使用方式,我认为将它们作为重点写出来很有必要。

使用 CSS Grid 来布局网站头部区域

这个问题是让我写下这篇文章的动机之一。我注意到我的弟弟使用 CSS grid 来实现网站的头部区域。

他辩解道“CSS grid 实在是太复杂,太难了“等等。由于使用了不正确的布局方法,他得到了一个想法,认为这是 CSS grid 太复杂造成的。其实不然,他所有的困惑都来自于把它用在不合适的地方的事实。

看一下我注意到的这个例子:

incorrect-use-1

.site-header {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr;
}

.site-nav {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr 1fr;
}

CSS grid 被使用了两次,第一次是用于整个标题,第二次是用于导航。他用 grid-column 来微调元素之间的间距,还有其他一些奇怪的东西,我记不起来了,但是最重要的你应该明白了吧!

使用 CSS Grid 在标签上

CSS grid 的另一个不正确的用法是将其应用于标签组件。请看下面的模拟图。

incorrect-use-2

下面是错误的 CSS 代码:

.tabs-wrapper {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
}

从上面的代码中,我可以看到,开发者假设标签数只有三个。因此,他用 1fr 1fr 1fr 来布置列。如果列数发生变化,布局就失效了。

过度使用 Flexbox 或者 Grid

记住,旧的布局方法可能是完美的方式。过度使用 flexbox 或 grid 会使你的 CSS 的复杂程度随着时间而增加。我并不是说它们很复杂,而是像本文中的例子所解释的那样,在正确的情况下正确地使用它们会好很多。

例如,你有如下的主要部分,要求将其所有内容水平居中。

img

我们可以通过 text-align: center 来实现这种布局,那么这个时候我们为什么要去使用 felxbox 这种更复杂的方式?

总结

关于使用 CSS Grid 和 Flexbox 之间的区别,我们已经说了很多了。这个话题我想了很久,我很高兴有机会写这个话题。如果有任何问题请不要犹豫,通过电子邮件或twitter @shadeed9 提供反馈!

感谢你的阅读!