阅读视图

发现新文章,点击刷新页面。
🔲 ☆

CSS六边形头像的实现与蜂巢布局

by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=12118
本文可全文转载,但需要保留原作者、出处以及文中链接,AI抓取保留原文地址,任何网站均可摘要聚合,商用请联系授权。

本文内容分为两啪,一个是六边形头像效果的实现,而是金字塔布局(又称蜂巢布局)的实现。

一、六边形头像

不啰嗦,直接看代码和最终实现的效果,同样的,用的是CSS corner-shape属性。

img {
  aspect-ratio: cos(30deg);
  border-radius: 50% / 25%;
  corner-shape: bevel;
  width: 150px;
  border: 1px solid #0001;
  object-fit: cover;
}

实时渲染效果如下:

如果你是手机访问,或者一些很久没升级的国产浏览器,应当看不到效果,可以看下面的截图:

六边形头像截图效果

六边形头像的CSS代码是固定的,大家使用的时候直接复制粘贴就好了。

二、蜂窝布局实现方法

六边形也正好是蜂巢格子的形状,因此,非常适合用来实现金字塔一样的蜂窝布局。

实际上,这种布局在日常开发中也是比较常见的,例如我最近开发的某个页面就有这样的布局:

金字塔布局示意

一般的开发人员遇到这种状况,可能会手工硬搓每个元素的定位,例如,例如匹配第一项元素,让其绝对定位居中,第二行元素保持Flex布局。

.item:first-child {
  /* 第一行特殊居中处理 */
  position: absolute;
}

其实可以试试Flex倒序排版。

Flex实现蜂窝布局

假设HTML结构如下:

<div class="container">
  <span>1</span>
  <span>2</span>
  <span>3</span>
</div>

则可以试试如下所示的CSS:

.container {
  --size: 40px;
  --gap: 5px;
  --offset: calc((2 * var(--size) + var(--gap)) / (-4 * cos(30deg)));

  width: 240px;
  display: flex;
  flex-wrap: wrap-reverse;
  direction: rtl;
  justify-content: center;
  gap: var(--gap);
  padding-bottom: calc(-1 * var(--offset));
}

.container > span {
  aspect-ratio: cos(30deg);
  border-radius: 50% / 25%;
  corner-shape: bevel;
  width: calc(var(--size) * 2);
  margin-bottom: var(--offset);
  /* 排序倒序 */
  order: calc(-1 * sibling-index());
  /* 提示文字居中 */
  display: grid;
  place-items: center;
  background-color: deepskyblue;
  color: #fff;
}

此时的渲染效果如下截图所示:

Flex实现的蜂巢布局

不过Flex倒序只适合三个数量,如果超过,那么这个布局方法就无效了。

下面问题来了,有没有什么办法,无论列表数量多少,自动金字塔布局呢?

Grid实现蜂巢布局

有,Grid布局是可以实现这样的效果的。

我们先从最简单三个列表项开始实现,假设HTML代码如下:

<div class="container">
  <s></s>
  <s></s>
  <s></s>
</div>

如下CSS代码就可以有蜂窝布局效果了:

.container {
  --size: 40px;
  --gap: 5px;

  width: 240px;
  display: grid;
  grid-template-columns: repeat(auto-fit, var(--size));
  justify-content: center;
  gap: var(--gap);
  padding-bottom: calc((2 * var(--size) + var(--gap)) / (4 * cos(30deg)));
  outline: 1px dotted;
}

.container > s {
  grid-column-end: span 2;
  aspect-ratio: cos(30deg);
  border-radius: 50% / 25%;
  corner-shape: bevel;
  /* 垂直方向间隙和gap保持一致 */
  margin-bottom: calc((2 * var(--size) + var(--gap)) / (-4 * cos(30deg)));
  background-color: deepskyblue;
}

.container > :nth-child(1) {
  grid-column-start: 3;
}

.container > :nth-child(2) {
  grid-column-start: 2;
}

原理很简单,只需要精确指定每一行第一个元素的grid-column-start值就好了,在Grid布局中,每一行后面的元素只会自动跟随排列的。

如果是三个列表元素,那么第一行的首元素序列是1,因此选择器是:nth-child(1),第二行的首元素序列是2,因此选择器是:nth-child(2),最后一个元素自动跟随,无需专门设置。

实时渲染效果如下:

不限数量全自动蜂巢布局

由于Chrome浏览器支持了if函数,因此,纯CSS实现不限数量全自动蜂巢布局成为了可能,具体实现代码如下:

@property --_n {syntax: "<integer>";initial-value: 1;inherits: true}
@property --_i {syntax: "<number>";initial-value: 1;inherits: true}
@property --_j {syntax: "<number>";initial-value: 1;inherits: true}
@property --_c {syntax: "<number>";initial-value: 1;inherits: true}
@property --_d {syntax: "<number>";initial-value: 1;inherits: true}

.container {
  --s: 40px;  /* 尺寸大小  */
  --g: 5px;   /* 间隙大小 */
  
  display: grid;
  grid-template-columns: repeat(auto-fit, var(--s) var(--s));
  justify-content: center;
  gap: var(--g);
  padding-bottom: calc((2 * var(--s) + var(--g)) / (4 * cos(30deg)));
  container-type: inline-size;
}
.container > * {
  grid-column-end: span 2;
  aspect-ratio: cos(30deg);
  border-radius: 50% / 25%;
  corner-shape: bevel;
  margin-bottom: calc((2 * var(--s) + var(--g)) / (-4 * cos(30deg)));
  --_n: round(down, (100cqw + var(--g)) / (2 * (var(--s) + var(--g))));
  --_i: calc((sibling-index() - 2 + (var(--_n) * (3 - var(--_n))) / 2) / (2 * var(--_n) - 1));
  --_c: mod(var(--_i), 1);
  --_j: calc(sqrt(2 * sibling-index() - 1.75) - .5);
  --_d: mod(var(--_j), 1);
  grid-column-start: 
    if(
      style((--_i >= 1) and (--_c: 0)): 2; 
      style(--_d: 0): max(0, var(--_n) - var(--_j));
    );
}

先是根据容器尺寸和元素尺寸计算每行可以显示的数量,然后根据取模的值是不是整数,判断是不是每一行的第一项,通过if()函数设置精准的grid-column-start值。

原理虽然简单,但是实现细节还是很复杂的,比如大家无需深究,直接复制粘贴代码使用就可以了。

只需要将子元素换成图片元素,就可以轻松实现下图所示的蜂巢头像布局效果。

蜂窝头像布局示意

具体不展开,因为受制于兼容性限制,目前只能实验环境使用。

三、结语说明

前端三剑客中,CSS的发展是最快的,你看我写的新特性介绍文章,大多数都是CSS,并不是我刻意挑选,而真TM就是大多数前端新特性都是CSS。

考虑到CSS的学习热潮早就沉寂多年。

我觉得CSS这门语言离断层不远了,只要几年不关注,我跟大家讲,那些前沿的CSS代码,绝对是看不懂的。

各种新函数、属性还有语法糖层出不穷,就好比本文这个金字塔蜂巢布局中的CSS实现细节,我估计9成以上的前端是看不懂什么意思的。

其中出现的这些特性,我之前都有介绍:

  1. corner-shape见此文:大开眼界的CSS corner-shape属性
  2. aspect-ratio见此文:Chrome 88已经支持aspect-ratio属性了,学起来
  3. round()mod()等数学函数:Chrome也支持round等CSS数学函数了
  4. cos()三角函数见:CSS sin()/cos()等数学三角函数简介与应用
  5. sibling-index()索引序号函数介绍出处:CSS索引和数量匹配函数sibling-index sibling-count简介
  6. if()函数介绍:CSS倒反天罡居然支持if()函数了
  7. container-type100cqw属于容器查询里面的知识:2022年最期待的CSS container容器查询

所以还是那句话,学习是不能停止的,时代变化很快,要是安于现状,说不定就会掉队。

参考文章:响应式金字塔网格

😉😊😇
🥰😍😘

本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
本文地址:https://www.zhangxinxu.com/wordpress/?p=12118

(本篇完)

🔲 ☆

CSS六边形头像的实现与蜂巢布局

by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=12118
本文可全文转载,但需要保留原作者、出处以及文中链接,AI抓取保留原文地址,任何网站均可摘要聚合,商用请联系授权。

本文内容分为两啪,一个是六边形头像效果的实现,而是金字塔布局(又称蜂巢布局)的实现。

一、六边形头像

不啰嗦,直接看代码和最终实现的效果,同样的,用的是CSS corner-shape属性。

img {
  aspect-ratio: cos(30deg);
  border-radius: 50% / 25%;
  corner-shape: bevel;
  width: 150px;
  border: 1px solid #0001;
  object-fit: cover;
}

实时渲染效果如下:

如果你是手机访问,或者一些很久没升级的国产浏览器,应当看不到效果,可以看下面的截图:

六边形头像截图效果

六边形头像的CSS代码是固定的,大家使用的时候直接复制粘贴就好了。

二、蜂窝布局实现方法

六边形也正好是蜂巢格子的形状,因此,非常适合用来实现金字塔一样的蜂窝布局。

实际上,这种布局在日常开发中也是比较常见的,例如我最近开发的某个页面就有这样的布局:

金字塔布局示意

一般的开发人员遇到这种状况,可能会手工硬搓每个元素的定位,例如,例如匹配第一项元素,让其绝对定位居中,第二行元素保持Flex布局。

.item:first-child {
  /* 第一行特殊居中处理 */
  position: absolute;
}

其实可以试试Flex倒序排版。

Flex实现蜂窝布局

假设HTML结构如下:

<div class="container">
  <span>1</span>
  <span>2</span>
  <span>3</span>
</div>

则可以试试如下所示的CSS:

.container {
  --size: 40px;
  --gap: 5px;
  --offset: calc((2 * var(--size) + var(--gap)) / (-4 * cos(30deg)));

  width: 240px;
  display: flex;
  flex-wrap: wrap-reverse;
  direction: rtl;
  justify-content: center;
  gap: var(--gap);
  padding-bottom: calc(-1 * var(--offset));
}

.container > span {
  aspect-ratio: cos(30deg);
  border-radius: 50% / 25%;
  corner-shape: bevel;
  width: calc(var(--size) * 2);
  margin-bottom: var(--offset);
  /* 排序倒序 */
  order: calc(-1 * sibling-index());
  /* 提示文字居中 */
  display: grid;
  place-items: center;
  background-color: deepskyblue;
  color: #fff;
}

此时的渲染效果如下截图所示:

Flex实现的蜂巢布局

不过Flex倒序只适合三个数量,如果超过,那么这个布局方法就无效了。

下面问题来了,有没有什么办法,无论列表数量多少,自动金字塔布局呢?

Grid实现蜂巢布局

有,Grid布局是可以实现这样的效果的。

我们先从最简单三个列表项开始实现,假设HTML代码如下:

<div class="container">
  <s></s>
  <s></s>
  <s></s>
</div>

如下CSS代码就可以有蜂窝布局效果了:

.container {
  --size: 40px;
  --gap: 5px;

  width: 240px;
  display: grid;
  grid-template-columns: repeat(auto-fit, var(--size));
  justify-content: center;
  gap: var(--gap);
  padding-bottom: calc((2 * var(--size) + var(--gap)) / (4 * cos(30deg)));
  outline: 1px dotted;
}

.container > s {
  grid-column-end: span 2;
  aspect-ratio: cos(30deg);
  border-radius: 50% / 25%;
  corner-shape: bevel;
  /* 垂直方向间隙和gap保持一致 */
  margin-bottom: calc((2 * var(--size) + var(--gap)) / (-4 * cos(30deg)));
  background-color: deepskyblue;
}

.container > :nth-child(1) {
  grid-column-start: 3;
}

.container > :nth-child(2) {
  grid-column-start: 2;
}

原理很简单,只需要精确指定每一行第一个元素的grid-column-start值就好了,在Grid布局中,每一行后面的元素只会自动跟随排列的。

如果是三个列表元素,那么第一行的首元素序列是1,因此选择器是:nth-child(1),第二行的首元素序列是2,因此选择器是:nth-child(2),最后一个元素自动跟随,无需专门设置。

实时渲染效果如下:

不限数量全自动蜂巢布局

由于Chrome浏览器支持了if函数,因此,纯CSS实现不限数量全自动蜂巢布局成为了可能,具体实现代码如下:

@property --_n {syntax: "<integer>";initial-value: 1;inherits: true}
@property --_i {syntax: "<number>";initial-value: 1;inherits: true}
@property --_j {syntax: "<number>";initial-value: 1;inherits: true}
@property --_c {syntax: "<number>";initial-value: 1;inherits: true}
@property --_d {syntax: "<number>";initial-value: 1;inherits: true}

.container {
  --s: 40px;  /* 尺寸大小  */
  --g: 5px;   /* 间隙大小 */
  
  display: grid;
  grid-template-columns: repeat(auto-fit, var(--s) var(--s));
  justify-content: center;
  gap: var(--g);
  padding-bottom: calc((2 * var(--s) + var(--g)) / (4 * cos(30deg)));
  container-type: inline-size;
}
.container > * {
  grid-column-end: span 2;
  aspect-ratio: cos(30deg);
  border-radius: 50% / 25%;
  corner-shape: bevel;
  margin-bottom: calc((2 * var(--s) + var(--g)) / (-4 * cos(30deg)));
  --_n: round(down, (100cqw + var(--g)) / (2 * (var(--s) + var(--g))));
  --_i: calc((sibling-index() - 2 + (var(--_n) * (3 - var(--_n))) / 2) / (2 * var(--_n) - 1));
  --_c: mod(var(--_i), 1);
  --_j: calc(sqrt(2 * sibling-index() - 1.75) - .5);
  --_d: mod(var(--_j), 1);
  grid-column-start: 
    if(
      style((--_i >= 1) and (--_c: 0)): 2; 
      style(--_d: 0): max(0, var(--_n) - var(--_j));
    );
}

先是根据容器尺寸和元素尺寸计算每行可以显示的数量,然后根据取模的值是不是整数,判断是不是每一行的第一项,通过if()函数设置精准的grid-column-start值。

原理虽然简单,但是实现细节还是很复杂的,比如大家无需深究,直接复制粘贴代码使用就可以了。

只需要将子元素换成图片元素,就可以轻松实现下图所示的蜂巢头像布局效果。

蜂窝头像布局示意

具体不展开,因为受制于兼容性限制,目前只能实验环境使用。

三、结语说明

前端三剑客中,CSS的发展是最快的,你看我写的新特性介绍文章,大多数都是CSS,并不是我刻意挑选,而真TM就是大多数前端新特性都是CSS。

考虑到CSS的学习热潮早就沉寂多年。

我觉得CSS这门语言离断层不远了,只要几年不关注,我跟大家讲,那些前沿的CSS代码,绝对是看不懂的。

各种新函数、属性还有语法糖层出不穷,就好比本文这个金字塔蜂巢布局中的CSS实现细节,我估计9成以上的前端是看不懂什么意思的。

其中出现的这些特性,我之前都有介绍:

  1. corner-shape见此文:大开眼界的CSS corner-shape属性
  2. aspect-ratio见此文:Chrome 88已经支持aspect-ratio属性了,学起来
  3. round()mod()等数学函数:Chrome也支持round等CSS数学函数了
  4. cos()三角函数见:CSS sin()/cos()等数学三角函数简介与应用
  5. sibling-index()索引序号函数介绍出处:CSS索引和数量匹配函数sibling-index sibling-count简介
  6. if()函数介绍:CSS倒反天罡居然支持if()函数了
  7. container-type100cqw属于容器查询里面的知识:2022年最期待的CSS container容器查询

所以还是那句话,学习是不能停止的,时代变化很快,要是安于现状,说不定就会掉队。

参考文章:响应式金字塔网格

😉😊😇
🥰😍😘

本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
本文地址:https://www.zhangxinxu.com/wordpress/?p=12118

(本篇完)

🔲 ☆

AI战争应用与中美安全困局

 一张摊开的旧式作战地图与纸面情报报告铺在木桌上,桌边摆着羽毛笔、指南针和几枚标记棋子,象征人工智能介入战争分析的开场氛围,羊皮纸,钢笔彩色手绘的统一风格。

大家好,欢迎收听老范讲故事的 YouTube 频道。

今天咱们来讲一讲:委内瑞拉和伊朗已经证明了,AI 应用于战争,所有人都在裸奔;中美博弈再起,委内瑞拉和伊朗终于证明了 AI 确实有用

刚才还有朋友在怀疑说,AI 到底有什么用,能干点什么?真的有用。马杜罗和哈梅内伊已经证明了有用。

Palantir 使用 Claude 大模型,开始为五角大楼服务,而且战绩彪炳。它做什么呢?多元数据摄入,把大量的数据拉回来,由 Palantir 进行融合和编排,Claude 做归类和归纳、推理、模拟、优先级排序,然后由人类指挥官来批准或者否决,最后执行平台行动。这是它的一个工作模式。

马杜罗的事情,很多媒体报道了 Claude 参与其中,做情报分析、建模和模拟。不是说盖了一个像马杜罗住所一样的房子,让军队在里面训练了几周,就专门训练这件事吗?到那以后,这些军人比马杜罗自己还熟悉他们家。包括作战规划,都是由 Claude 去完成的。最终还做了一些网络上的对抗,这都是 Claude 干的活。

哈梅内伊的事情,没有明确报道,但是从时间来看,前期情报处理和分析,甚至是确定按钮的,应该也是 Claude。2 月 28 号攻击的,临时起意,准备其实并没有那么充分。国内也有很多报道,说是发现哈梅内伊要开会,要准备离开地堡,在短暂的一个时间窗口里可以确定他的位置,那么就决定了,在没有完全准备好、很多飞机航母都没到的情况下,就开始开打了。提供情报汇总、分析、评估的,大概率还是 Claude。

虽然 Anthropic 后来跟战争部闹翻了,川普签字说我们要把它干掉,但是不是很短时间之内就可以真正进行替换和整合的。像这种东西的底层替换,一定是换过来以后要做大量测试,才可以顺利跑起来。所以这件事情证明了 AI 真的有用。

今天这个故事,咱们分 6 段来讲。

  1. AI 应用于战争,可以证实或者有传闻的消息有哪些。
  2. 金融时报报道了美国五角大楼要针对中国电网、电力设施、算力中心,组织 AI 公司进行攻击。
  3. 为什么在 AI 面前,所有人都在裸奔。
  4. 周鸿祎建议建立防范 AI 的一个新模式,这个模式最不靠谱,但是最有可能被采用。
  5. 中美两国根据各自国情制定的不同战略,也都是无奈之举。
  6. 最后,最好还是不要打仗,愿世界和平。

第一段:AI 用于战争,可以证实或者有传闻的消息有哪些?

一间昏黄灯光下的情报室,墙上挂着中东与南美地图,桌面堆满卫星照片、通信记录和带箭头的关系图,两名军官凝视一块写满推演步骤的黑板,羊皮纸,钢笔彩色手绘的统一风格。

第一个,美军确实已经把 Pioneer 加上大模型推进了作战支持层,使用的是 Anthropic 的 Claude 大模型。这个路透社和 Anthropic 都已经证实了。2024 年 5 月份,Palantir 就跟国防部签了 4.8 亿美元的合同,去做叫 MAVN 的智能系统,这个已经确认了,而且做了好几年了。

Claude 参与了抓捕马杜罗的行动,这也是路透社引述了华尔街日报的报道,也基本上实锤。但是具体在里边是怎么做事情的,并没有一个确切的报道。实际上,在这个里边,Palantir 做的工作还是很多的。大量的数据怎么进行整理,怎么去写提示词,怎么去做提示词工程,甚至怎么绕过 Claude 的各种限制,这都是 Palantir 干的活。这块就没有特别详细的介绍了。

第三个,Claude 被用于对伊朗作战这件事,路透社是有公开报道的。但是针对哈梅内伊本人的行动,是不是有 Claude 的参与,没有明确报道。这种事情,从逻辑上推断,应该是 Claude 干的活。但是这个活如果明确了,Anthropic 即使是在美国的员工,可能也会有生命安全方面的问题需要担忧了。因为你把神棍干掉了,他们还是有可能会报复的。

战争部已经开始用 Claude 6 了,这也是一种传闻。为什么会有这样的传闻呢?咱们要知道,现在我们使用的 Claude 最新版本是 4.6 Opus、4.6 Sonnet,4.6 Sonnet 比 4.6 Opus 还要稍微晚一点点。但是大家都会觉得,各种前沿工具应该首先用于军事,包括航空航天,军事用完了以后再去民用。美国国防部使用的版本,确实跟咱们用的不一样,人家使用的是 Claude GOV,政府专用版。这个版本可能会比我们民用版本要高很多。这个事符合逻辑与预期,但是没有实锤。Anthropic 自己也没有承认,政府官员,包括科技媒体、严肃媒体,都没有出来确认这个事情,只是大家觉得应该是这样的。

还有什么呢?华为曾经在伊朗战争之前发出过警告,说以色列军方正在搜集二手的华为手机。这个也是一个传闻,并没有一个实际的确定。伊朗是被禁运了很久了,内部肯定是有通讯设备更新换代的需求。以色列军方搜集二手的中国设备,特别是华为手机,安装后门之后运往伊朗,也不算是一个超出逻辑判断范围的事情。因为以色列曾经搞过寻呼机爆炸案,炸死了哈马斯的高层,所以这个传闻估计是顺着这个逻辑编出来的,或者是产生出来的。所以它到底是真是假,我们没法去验证。

还有一种可能是什么呢?就是华为也需要为大批出现在伊朗的设备找个借口。他原来也讲说,我没有直接向伊朗卖东西,结果到那以后发现这么多华为设备。那以色列军方为了装后门,他们送去的,这也算是一个说法吧。

Palantir 跟 Anthropic 的分工到底是怎么干的?美军到底是怎么用这个玩意的?Palantir 负责把传感器、数据库、通讯记录、卫星视频、文本情报、历史目标库等等,变成统一的操作界面,就是它负责前期的数据整理。而 Claude 是把这些对象关系快速讲清楚,排序归纳,做模拟。这就是他们两个的合作方式。甭管是马杜罗的事情,还是哈梅内伊的事情,就是他们两个无间配合所得到的战绩。这是目前为止能够确认的和有传闻的 AI 参与战争中的一些故事。

至于说华为号称以色列在收集二手华为手机,这事跟 AI 有关系吗?也有关系。没有 AI 的话,这么多设备扔进去以后,你根本没有办法进行数据整理。所以这应该也是一个 AI 故事。

第二段:金融时报报道了美国五角大楼要针对中国电网、电力设施、算力中心,组织 AI 公司进行攻击

一幅俯视视角的城市电网与数据中心示意图,变电站、高压线路和服务器机房被红蓝两色墨线标注,几只机械风格的侦察无人机在上空盘旋,羊皮纸,钢笔彩色手绘的统一风格。

这个是什么时候报道的?是 2 月底,有这样一份报道。而且包括路透社什么的都转载了这样的文章。可惜这文章是个付费文章,我没有看全文,我的“龙虾”也没有看到全文。现在其实我的阅读已经越来越碎片化了,原来还会找一些完整的文章来看,现在通通都是 GPT 或者“龙虾”,你就把这事搞定了就完事了。

但是,这个文章里头现在可以确定的几点:

  • 第一个,目标是非常具体的。不是说我们有 AI,我们看看怎么办,非常非常具体。报道写的是中国的电网、电力公用事业网络、敏感网络,不是泛泛而谈,我们就是要攻击这些东西。
  • 第二个,这个是很重要的,任务不是防御,而是面向未来冲突的进攻性准备。我要攻击你。金融时报提到的是自动侦查、识别软件漏洞、加强渗透,在冲突中破坏这些系统,并将潜在目标整合到美国的战争计划中。
  • 第三个,参与讨论的美国头部人工智能公司有哪些呢?金融时报点名的是 OpenAI、Anthropic、谷歌和 xAI。这个 Anthropic 不是说我们不去做这个事吗?人家说的是我们不对美国公民做大范围监控,可没承诺说不对中国公民做大范围监控。所以双标还是在这里的。这个事情,Anthropic 还是参与了。至于后边特朗普还让不让它参与,那是另外一回事了。

与此同时,路透社也报道了五角大楼一直在推动这些公司将模型接入保密网络,并希望减少常见的使用限制,用于未来战争,包括自主无人机群、机器人、网络攻击在内的场景。但是这一块里头,Anthropic 可能就还是要有一些不那么配合的地方。

第四个,这件事情和 Anthropic 的冲突直接相关。Anthropic 坚持两条红线:在美国国内大规模监控它不干,完全自主武器它也不干。而五角大楼希望以“所有法律允许应用”的口径更广泛地使用。

第三段:为什么在 AI 面前,所有人都在裸奔

一座由无数密码纸条、手机定位点、社交关系线和数据库表格拼成的透明玻璃屋,屋内人物无处隐藏,屋外一只巨大的机械眼正在逐层扫描,羊皮纸,钢笔彩色手绘的统一风格。

为什么在 AI 面前,所有人都在裸奔呢?我们做好防备不就行了吗?事情没有这么简单。不是说我们看到马杜罗被抓了,看到哈梅内伊被炸死了,我们也好好做好防备,这个事就可以防得住,不是这么回事。中国防不住,美国也防不住,全世界任何一个国家,你都防不住这个东西。

原因很简单,绝大部分的网络攻击,跟谁的技术强、谁的技术弱,没有任何关系。90% 或者 80% 以上的网络攻击,都是通过社会工程学漏洞来搞定的。什么是社会工程学漏洞?弱口令,不同设备使用相同的口令,将敏感情报随意放置,随口将保密信息泄露给家人。只有很少很少的部分跟技术相关,绝大部分都是这个。

那你说加强管理不就可以了吗?不可能,这事也是不可能解决的。

规矩越严,漏洞越多

一名文员面对十把不同锁头和一摞写满复杂密码的小纸条,桌边散落着笔记本与登录卡片,背后高墙上挂着严苛安全规定告示,羊皮纸,钢笔彩色手绘的统一风格。

第一个,规矩越严,漏洞越多。这个是不是跟所有人的看法正好相违背,或者说超出大家认知了?我们把规矩定严一点,为什么漏洞反而变多了呢?原因很简单,我们管得越严,越违反人性。比如说,一个 6 位的密码,我脑子能记住,没毛病。10 位的密码,老范反正记忆性不好,勉强也能记住吧。但是 10 个不一样的密码,我要给 10 个设备或者 10 个需要登录的地方设 10 个不同的密码,每个月还要进行更新,还不允许重复,以前用过的不许用,这 10 个密码都不能一样,老范记得住吗?别说老范了,我估计能记住的人不多。那咋办呢?写个小纸条吧。估计小纸条都不行,得写个本子,否则这玩意没法整。你说我还需要编个 10 位的数,编完了以后还能让它不重复,这漏洞不就出来了吗?就是你管得越严,漏洞就越多。

那大家说不是一直都这样吗,怎么今天就裸奔了,就不行了呢?原因很简单,原来没有 AI,想要将这么多的数据进行分类、建模、对比和评估,基本上是不可能的。所以以前都是首先靠刑侦专家,这个有可能有事,那个有可能有事,再找数据专家进行小范围的数据整合。这些数据专家怎么干活的?他把需要整合的数据先记在脑子里,然后寻找规律。你脑子到底能记住多少数,这是有限的。所以数据专家不是那么好当的。

现在有 AI 了,所有人就只能裸奔了。我们经常讲的大数据,到了 AI 这里才是真正的大数据。不是数据多就可以叫大数据的。大数据有一个很重要的特征,叫全量数据,就是要所有的数据搁在里头一起算,才叫大数据。你原来虽然是大数据,但是在处理的时候还是要靠人,先去抽样,然后去寻找规律,找完规律了以后再拿全量数据去计算。现在的话,你不需要了,直接把全量数据通通扔给 AI,让它去进行筛选,让它去找其中的逻辑,比人的效率要高无数倍。

AI 可以获得什么数据来进行分析

现在 AI 可以获得什么数据来进行分析呢?

  • 第一个,是大规模的历史数据。你不要想着说我从今天开始把这事改好了就完事了,那你以前的这些数据咋办呢?我原来在哪哪设置个口令,这口令可能还泄露了;我以前申请过什么证件;我在哪做过什么样的事情。这些历史数据你是抹不掉的。历史数据唯一处理的方式是什么?叫逐渐失效。比如说我曾经登记过我住在什么地方,过一段时间我搬家了,那这个历史数据就会失效掉。但是这个失效的过程,第一个很慢,第二个完全不可控。
  • 第二个,AI 是可以获得大量的弱身份认证验证的。你说我用身份、用生日去做这个密码,用我儿子的生日去做密码,用我儿子的名字去做密码,那对于 AI 来说,实在是不要更好猜了吧。
  • 还有什么呢?就是很糟糕的网络分段。网络经常说我们这是物理隔离的,还是怎么的,但其实很多这样的机器还是会突然想看个片子,想打个游戏,想去下载一个开源软件包。在这样的情况下,这个数据就会泄露出去。
  • 还有一种东西叫数据经销商。有很多人会来回卖这些数据。在中国可能要更严重一些,在美国这几年治理得还稍微好一点点。所以数据经销商也是在这里边推波助澜。
  • 还有一些就是长期潜伏式的渗透,各种设备、各种沉睡设备,这块也是会被 AI 拿出来,得到所有这些数据去进行分析的。

所以你说 AI 知道马杜罗的各种情况、行程,AI 也知道哈梅内伊的情况,这个太正常了。你根本不可能把这个痕迹抹干净,就算你今天抹掉了,你以前呢?你前面这些信息,你根本抹不掉。

所以 AI 提供的是低成本推理和极高程度的自动化。你拿到这些数据以后,那就没办法了。原来因为人手不够而忽略掉的大量细节,就可以被 AI 发现和处理了。

一些具体的数据来源

比如我们在电商网站上的收货地址,美国人只要是买过中国货的,他们的收货地址大概率都在中国存着。因为最早期大家在亚马逊上买东西,在包括 Temu、TikTok Shop 这上头去买东西的时候,中国商家都是会得到你们的收货地址的。这是张三还是李四,住在哪个州、哪个市、哪个街道,你们的电话号码是多少,你的邮政编码是多少,你到底买了一个什么东西,这些数据都存在。这些数据在中国是可以买到的,虽然不能公开买,但是确实可以买到,而且还很便宜。

还有什么数据会被发现呢?比如手机中的 WiFi 基站信息。原来咱们经常讲一个笑话,有一个女生带着老公去闺蜜家玩耍,结果她老公到了闺蜜家以后,手机自动就连到 WiFi 上了,那大家猜这是怎么回事。所以我们的手机里头,到底连接过哪些 WiFi 基站,在什么样的时间里头到过哪些 WiFi 基站,包括到过哪个移动基站,手机里都是有记录的。

还有什么呢?就是全量的互联网和社交媒体上的这种数据。因为这都是本身公开数据,一定会有大量的爬虫,有大量的机构在收集这些信息。现在这些通通都可以被 AI 进行处理。

关键基础设施的工作和管理人员,他们的所有行动轨迹、人际关系都可以快速地被标记出来,然后就可以进行筛选、评估、渗透或者策反了。这个就是前面五角大楼说,我们把中国电网的这些管理人员,或者他们的这些弱口令都算出来,我们把他们的这些亲友关系也都找出来,看看他们是不是向亲友泄露了什么信息了,然后我们看看他们这些亲友有没有出去暴露说我老公一个月挣多少钱,类似这样的事情,这个就都可以去进行筛选了。这个确实是有效的。

Anthropic 在今年 2 月份,他们一个研究员发论文,应该叫《利用大语言模型进行大规模在线去匿名化》。在大模型跟 agent 的帮助下,网络上的化名、伪匿名,正在变得更加容易批量被重新识别。比如说大家在 Reddit 上起了各种各样的网名,在里头去聊天。我们在国内其实无所谓,因为国内本来就是实名制的。你比如我在 X 上注册了一个账号在那聊天,但是现在有 AI 了以后,很快就可以一一对应张三李四。你到底在什么地方,你曾经说过什么,后来说过什么,你一共有几个账号,飞快就都给你对应上了。所以媒体对这个研究的概括就是:匿名虽然还没有死亡,但是已经更加脆弱了。

为什么咱们这个标题叫裸奔呢?就是所有人都是在裸奔,不要有任何的侥幸心理。是不是像中国这样搞实名制都没有任何区别?你说中国实名制好邪恶,美国人不搞实名制。你上了 AI 以后,大家其实也都已经实名制了,都是被动实名制了。

第四段:周鸿祎建议的防范方式,为什么最不靠谱,但是最有可能被采用

一座巨大的中央监控大厅里,成排屏幕显示日志、轨迹与身份数据,中央高台像堡垒一样汇聚所有信息,周围却出现多条隐秘裂缝与渗透箭头,羊皮纸,钢笔彩色手绘的统一风格。

那下一件事,咱们来讲一讲周鸿祎建议的这种防范方式,为什么最不靠谱,但是最有可能被采用。

周鸿祎建议什么呢?就是进行大范围的监测。怎么说呢,360 确实是有能力来干这个活。他为什么要做大规模的这种监测,待会咱们再讲。而且周鸿祎现在也确实是在提这件事。因为周鸿祎是九三学社界别的政协委员,现在正在开两会,他现在是准备提这个提案了。

为什么大规模侦测可以防范 AI 数据分析呢?原因很简单,就是 AI 分析评估之后,获得了可能的攻击路径。我觉得你的这个弱口令可能是这个,可能是那个。得到这些信息了以后,你要干嘛?你要做测试。你不能说我不试,等到打仗的时候我再试,这事肯定不行。测试可用的,我才能够记录下来,以备不时之需。万一我要打仗呢,我就要拿你的密码去处理这个事情。哪怕说我今天开始积累了,过两天你离职了,但是经常还发生这种事情:人离职了,他的账号依然有效。这种事情其实是特别特别多的。那没准到打仗的时候,我们就用以前离职员工的账号,登进去做了一些什么事情。

这种测试就是一定要先做。这种测试通常是很大规模的,而且会有一定的规律。如果进行广泛的监控的话,就有可能提前发现这些测试行动。后面的话就是传统的攻防策略了。比如测试的时候我们怎么进行混淆,而 360 就是说我怎么在各种混淆的数据里头再把它们抓出来。这个实际上就是 360 的老本行了。

为什么说这种方式最不靠谱

那你说为什么说这种方式最不靠谱呢?咱们讲了半天,好像还行,听着有点道理。

  • 第一个,是根本性的问题根本得不到解决。比如弱口令,该是弱口令还是弱口令;旧数据,这些人以前做过什么样的事情,以前留了哪些痕迹下来,你也不可能把它消除掉;还有一些旧设备,比如有一些设备上记录了我以前的某一个密码,但是我离职了,这个密码还记在那个设备上,这个设备你也不可能把它扔了。这些都是不会有任何变化的。
  • 第二个,就是有钱的单位可以以旧换新一次,也不能说一点用没有,可能稍微有点用处。但是以旧换新这件事,可能更多的还是大家洗了一把钱,把钱揣在口袋里了。

最重要的问题是什么?就是大范围监控会带来比以往更加严重的新的风险。那什么风险?是不是隐私泄露,还是什么?不是这样的。一旦大范围监控,意思就是什么?就是要进行大集中。我们要统一地来做这个事情,我们要将这些日志、行为、身份、轨迹、设备数据通通都集中在一起,等于新增了一个更大、更高价值的靶子。我们再攻击它不就完了吗?

而且这种机构里边的权限治理、外包链路、内部审计,在中国都不是通过那么符合逻辑的方式来设置的。可能谁家的亲戚,谁家的小舅子、小姨子,他可能进来了;外包的时候可能也是层层转包了以后,所以这种机构本身的安全性并不高。所以这个玩法会极大地放大泄密和滥用的可能性。其实很多的数据,特别是在公安机构或者类似这样的机构,反而容易泄露,就是他们经常会滥用这些数据。你这刚说点什么事,下边就有人来找你了,就是这个数据被滥用了。既然可以被滥用,那它被攻击的可能性就更大一些,而且更脆弱一些。这个大家应该是可以理解的。

所以为什么说这是最不靠谱的一种方式。那为什么这种建议还是最有可能被采用的呢?这里是一个不断中心化的国家,我们尽可能地要把权力收束在最中间那个地方。可以搞大集中,可以把数据权力收束到更上层,这是一个符合国家策略的决策。而且又可以花钱,这总不是坏事;还有美国人当靶子。给钱花、集权,这就是为什么我们讲说这是一个最不靠谱,但是最有可能被采用的方案。

第五段:中美两国根据各自国情进行不同的战略,也都是无奈之举

一张对比式画面,左侧是高墙、关卡与集中机房构成的封闭系统,右侧是分散的家庭路由器、云服务器和供应链箱体连成网络,两边都被细小的数据线缠绕,羊皮纸,钢笔彩色手绘的统一风格。

再往后,咱们来讲一讲中美两国根据各自国情进行不同的战略,但是也都是无奈之举。

中国的战略其实很简单,就是建墙,做各种各样的限制。美国的战略就是发禁令,这个不许买,那个不许用,搞这样的方式。

为什么是这样的?这两个国家最大的差异到底是什么呢?美国大量的网络基础设备,很多家用路由器都是中国造的,想要替换非常困难。华为通讯设备已经基本上替换完了,TP-Link 的家用路由器在美国应该现在还是最受欢迎的前几名之一吧。

我昨天还在 YouTube 上看到了大量在卖美国家庭 IP 的广告。什么叫卖美国家庭 IP?就是我们想去访问美国的一些数据,但是如果我通过亚马逊云、Oracle 云、谷歌云去干这个活的话,这事是不行的。因为云机房的 IP 地址,很多公司或者很多服务会去侦测。如果是云计算机房的 IP,它就不给你服务了。你要最好的方式是什么呢?就是去买美国家庭 IP。那你说我又不住在美国,我怎么买到美国家庭 IP?很简单,就是他卖这种有后门的路由器进去,大家使用这个路由器以后,这些 IP 地址就可以去销售了。而且他卖这个广告,是在 YouTube 上公开销售,这个很神奇。

电动车,美国基本上没有引进,这件事他们还算是比较谨慎,不像中国似的引进了一大堆特斯拉进来。但是美国大量的电力基础设施、交通设施、航运设备,也都是使用中国制造的。想要替换,没有那么容易。川普说了,我们要把这东西都换掉,包括原来拜登也说我们要把这东西都换掉。但是开什么玩笑,你有那钱吗?就算有钱,你也得让日本的工厂、美国的工厂先缓个几年,才有可能重新再制造这些东西。现在这些东西都是中国大央企造的,还不是普通企业。所以这一块是美国现在面临的最主要的问题。

而且因为电商和社交媒体,大量美国人的个人信息都是在中国有保存的。比如说大家的快递单、购物清单都存在中国,而且存了很多份。这些东西还在地下市场进行高效的交易。即使现在进行清理,但是旧数据也是不可能消失的。刚才咱们讲了,旧数据唯一的处理方式就是逐渐失效,而且失效的过程完全不可控。

中国是什么样的情况呢?中国其实搞了好几年的信创产业了,就是完完全全用中国自己的东西去替换,进行了有计划的替换。现在关键部门的设备国产化率已经非常非常高了。中国直接就搞实名制了,我不跟你费这个劲。而且咱们玩 GFW,就是防火墙。还有,是国家直管,这是咱们现在玩的方式,跟美国其实完全是两个方式在运作。

那说中国的数据美国有没有呢?肯定也有。那么多操作系统,那么多美国的开源软件,那么多美国的设备在中国也在跑着。所以数据这件事,中国肯定是拿到了美国大量的数据,反过来美国也拿到了中国大量的数据,基本上没有什么秘密可以去保。你说我现在一定要保密怎么办?新发生的事情,我现在从现在开始注意,它确实是可以保密。但是你跟过往的这些痕迹去结合了分析以后,也很难。所以中美两国现在就是这么一个状态。

中国的模型虽然没有美国的好,但是咱人多。那么多大学生毕了业还没有工作,那么多牛马,你只要敢真的用他们,这件事也不是说搞不起来。

真正的差异:集中和分散

真正中美两国的差异在什么地方呢?中美两国的差异,叫集中跟分散的差异。中国叫不断地集中,制造一个个难以审计、容易渗透的数据集中点。下属对上层必须要绝对透明,你不能对上层拥有任何秘密。很多本身逻辑难以自洽的交易,反而增加了很多数据漏洞。有很多人说,我现在要拿这个数据去干点什么事,那这些数据的泄露风险就会进一步上升。

信创产业其实也是一个很难逻辑自洽的东西。第一个是大量的开源软件被包装成自主研发的软件。你比如说我们把 Linux 包一包,说这就是我们自己研发了,咱们上吧。甚至这些软件现在可能都是用 Claude Code 去写的,大家就很相信这个东西里头一定没有后门吗?反正我是没有那么自信了。而且我们通过信创产业,把很多算力很低下、充满漏洞的这些设备直接替换上去了。所以这个事情其实未必真的能够像我们一开始设想的那么有用。

美国玩的是另外一条路。咱们是尽量集中,他是尽量分散。美国司法部要求数据最小化,少留、少集中、少长期保存,该删就把它删掉。司法部已经明确了,把批量敏感数据加上 AI 视为国家安全的放大器,说这件事太危险了,能删咱就删了,能不要留着就别留着。

  • 身份与权限必须要重构,做最强的多重认证。比如输了密码以后,再拿个手机认证,或者再拿一个什么其他的设备再去认证一下。而且要最小给权限,尽量不要给。所有的这些令牌都是短期的,今天能登录,明天就不许登录了,用这样的方式来避免弱口令的问题。关键的岗位必须是双人批准,你不能说很关键的一个位置只有一个人,这事是不行的。
  • 但是在中国肯定没法整,我们一定是要求一个人说了算。甭管是在哪一个层级,都必须要一个人说了算。只要是有两个人说了算的,那这事都没法办了。所以美国人就跟咱们反着来。
  • 中国其实也做了很多这种多重认证的要求,但是你只要有一个实名制在这,你再怎么多重认证都是白费的,都很容易就被一一对应起来了。所以这个事对于我们来说,稍微有一点点难度。
  • 还有网络分段和关键设备的隔离。要把办公网络、生产网络和训练网络、推理网络、工业运行的网络断开。这是美国的要求,你必须把它分开,特别是把工业运营的这种数据,要使用独立的 AI 系统,你不能跟大家使用同样的 AI 系统去工作。关键角色必须保证由人来做。
  • 供应链审查,这是美国做的最核心的事情。硬件、固件、更新机制、实验室认证、云服务、外包商,一起审。像华为、TP-Link 的争议说明,今天风险已经深入到整个供应链里头去了。所以美国说从今天以后我们不再买了,但是原来买的这些东西,华为他们剔掉了,但是像 TP-Link 还有很多这些网络设备,其实还在那开心地运营。即使是一些号称美国生产或者印度生产的设备,其实里边也有大量零部件,或者一些软件还是来自中国,他们很难躲开的。
  • 最后,他们是要做端到端的加密,以及高敏沟通的迁移。就是敏感度比较高的沟通,我们要尽量挪开。这件事在中国就比较难了。第一个,是下层针对上层是不能有秘密的,所以你做端到端加密了以后,你到底想对谁保密?你是不是想搞小集团?这事肯定不行。另外,在没有那么多信任的情况下,特别是中间集中的这样的情况,你就得开会。很多人说哈梅内伊到这样的情况,为什么要集中起来开会?没办法,他不信任其他人,必须要让大家坐在我面前,看着我把这会开了。他是这样的一个状态。所有这种集中式的管理,有些东西你是避免不了的,就得开会,就得聚集。
  • 美国人还会干一些什么呢?就是 AI 专项的红队与审计日志,不是只防人,也要防模型被提示词操控、数据污染、越权调用等等。越权调用和权限滥用这件事,在中国其实也是很难杜绝的。
  • 还有最后一点是什么?在法律上,美国尽可能地要切断数据经营链子。卖数据这个事是不行的。美国这两年真正补上的就是数据,不是说在美国数据就不许卖了,只是卖给外国对手、批量敏感数据和政府相关数据,这三件事情,美国在认真整治,已经整了两年了。

中国这方面,国家层面仍然在推动数据要素市场化配置和合规数据流通交换交易,就是我们现在还在努力地去卖数据。当然了,我们也在讲个人数据、重要数据跨境提供,这些数据需要单独地去进行审批。只是我们的执法力度还要稍微再跟上一点点。现在每年 315 还在讲说我们怎么买数据这个事情。既然每年都可以到 315 上讲,那么在中国数据市场外流通这件事情还是非常非常严重的。

后面就是考验中美两国的行政和执法能力的时候了。其实都防不太住,美国也防不太住,中国也防不太住。

第六段:最好还是不要打仗,愿世界和平

夜空下的地球被柔和光晕环绕,前景是一张收起的作战地图与放下的长剑,远处火箭正向星空升起,象征放弃冲突、转向和平与探索火星,羊皮纸,钢笔彩色手绘的统一风格。

最后还是讲,最好就不要打仗。AI 其实有点像核武器,这件事 Anthropic 的 CEO 达里奥·阿莫迪倒是没有说错。当时他讲,向中国卖 H200 就是向朝鲜提供核武器,这件事是对的。核武器真正的作用,并不是决定战争的结局,而是止战。一旦有核武器以后,就不能再去打世界大战了。因为打了世界大战,有可能人类就毁灭了。所以从二战以后,我们就再也没有打过世界大战,只是做一些小范围局部战争,或者这种代理人战争,大的就没了。

AI 其实也是如此。伤害巨大,甚至有可能会造成一定的灭绝,基本上还没法防范。这就是 AI 现在跟核武器一致的地方。美国的家庭 IP 还在美国的 YouTube 网站上公开叫卖,港口设备基本上 100% 是中国制造,没个几十年根本就没有办法去替换。中国集中管理确实会让站在上面的人有一种很强大,甚至无所不能的幻觉,但同时也造成了非常非常多的脆弱点。

AI 已经这么强大了,大家还是世界和平,别折腾了,行不行?我们一起研究一下怎么去火星吧。这就是咱们今天要讲的故事。

🔲 ☆

当 Go 遇上 GPU:用 CUDA 释放千倍算力的实战指南

本文永久链接 – https://tonybai.com/2026/01/21/integrating-cuda-in-go

大家好,我是Tony Bai。

长期以来,高性能计算(HPC)和 GPU 编程似乎是 C++ 开发者的专属领地。Go 语言虽然在并发和服务端开发上表现卓越,但在触及 GPU 算力时,往往显得力不从心。

然而,在最近的 GopherCon 2025 上,软件架构师 Sam Burns 打破了这一刻板印象。他展示了如何通过 Go 和 CUDA 的结合,让 Gopher 也能轻松驾驭 GPU 的海量核心,实现惊人的并行计算能力。

本文将带你深入这场演讲的核心,从 GPU 的独特架构到内存模型,再通过一个完整的、可运行的矩阵乘法示例,手把手教你如何用 Go 驱动 NVIDIA 显卡释放澎湃算力。

img{512x368}

为什么 Go 开发者需要关注 GPU?

在摩尔定律逐渐失效的今天,CPU 的单核性能提升已遇瓶颈。虽然 CPU 拥有极低的延迟、卓越的分支预测能力和巨大的缓存,但它的核心数量(通常在几十个量级)限制了其处理大规模并行任务的能力。

相比之下,GPU (Graphics Processing Unit) 走的是另一条路。它拥有成千上万个核心。虽然单个 GPU 核心的频率较低,且缺乏复杂的逻辑控制能力,但它们能同时处理海量简单的计算任务。这使得 GPU 成为以下场景的绝佳选择:

  • 图形处理与视频转码
  • AI 模型推理与训练(神经网络本质上就是大规模矩阵运算)
  • 物理模拟与科学计算(如流体力学、分子动力学)
  • 密码学与哈希碰撞

通过 Go 语言集成 CUDA,我们可以在享受 Go 语言高效开发体验(构建 API、微服务、调度逻辑)的同时,将最繁重的“脏活累活”卸载给 GPU,实现 CPU 负责逻辑,GPU 负责算力 的完美分工。

GPU架构与CUDA编程模型速览——理解 GPU 的“兵团”

在编写代码之前,我们需要理解 GPU 的独特架构。Sam Burns 用一个形象的比喻描述了 GPU 的线程模型。如果说 CPU 是几位精通各种技能的“专家”,那么 GPU 就是一支纪律严明、规模庞大的“兵团”。

而指挥这支兵团的指令集,我们称之为 “内核” (Kernel)

0. 什么是 Kernel?

此 Kernel 非彼 Kernel(操作系统内核)。在 CUDA 语境下,Kernel 是一个运行在 GPU 上的函数

当我们“启动”一个 Kernel 时,GPU 并不是简单地调用这个函数一次,而是同时启动成千上万个线程,每个线程都在独立执行这份相同的代码逻辑。每个线程通过读取自己独一无二的 ID(threadIdx),来决定自己该处理数据的哪一部分(比如图像的哪个像素,或矩阵的哪一行)。

1. 线程模型:从 Thread 到 Grid

理解了 Kernel,我们再看它是如何被调度执行的。CUDA 编程模型将计算任务分解为三个层级:

  • 线程 (Thread):GPU 工作的最小单位。它类似于 CPU 的线程,但极其轻量。每个线程都有自己的 ID,负责处理数据的一小部分(例如图像中的一个像素,或矩阵中的一个元素)。
  • 块 (Block):一组线程的集合。一个 Block 内的线程运行在同一个流式多处理器 (SM) 上。关键点在于:同一个 Block 内的线程可以通过极快的“共享内存”进行协作和同步(__syncthreads())
  • 网格 (Grid):所有执行同一个内核函数(Kernel)的 Block 的集合。Grid 涵盖了整个计算任务。

2. 内存模型:速度与容量的权衡

GPU 的内存架构比 CPU 更为复杂,理解它对于性能优化至关重要:

  • 寄存器 (Registers):最快。每个线程私有,用于存储局部变量。数量有限,用多了会溢出到慢速内存。
  • 共享内存 (Shared Memory):极快(L1 缓存级别)。属于 Block 私有,是线程间通信的桥梁。优化 CUDA 程序的核心往往在于如何高效利用共享内存来减少全局内存访问。
  • 全局内存 (Global Memory):较慢(显存,如 24GB GDDR6X)。所有线程可见,容量大但延迟高。
  • 常量内存 (Constant Memory):快(有缓存)。用于存储只读参数,适合广播给所有线程。

编写高效 CUDA 代码的秘诀,就是尽可能让数据停留在寄存器和共享内存中,减少对全局内存的访问。

Go + CUDA 实战——跨越鸿沟

理解了原理,现在让我们动手。我们将构建一个完整的 Go 项目,利用 GPU 并行计算两个矩阵的乘积。这个过程需要借助 CGO 作为桥梁。

1. 项目目录结构

go-cuda-cgo-demo/
├── main.go       # Go 主程序 (CGO 入口,负责内存分配和调度)
├── matrix.cu     # CUDA 内核代码 (在 GPU 上运行的 C++ 代码)
└── matrix.h      # C 头文件 (声明导出函数,供 CGO 识别)

2. 编写 CUDA 内核 (matrix.cu)

这是在 GPU 上运行的核心代码。我们定义一个 matrixMulKernel,每个线程利用自己的坐标 (x, y) 计算结果矩阵中的一个元素。

// matrix.cu
#include <cuda_runtime.h>
#include <stdio.h>

// CUDA Kernel: 每个线程计算 C[row][col] 的值
__global__ void matrixMulKernel(float *a, float *b, float *c, int width) {
    // 根据 Block ID 和 Thread ID 计算当前线程的全局坐标
    int row = blockIdx.y * blockDim.y + threadIdx.y;
    int col = blockIdx.x * blockDim.x + threadIdx.x;

    if (row < width && col < width) {
        float sum = 0;
        // 计算点积
        for (int k = 0; k < width; k++) {
            sum += a[row * width + k] * b[k * width + col];
        }
        c[row * width + col] = sum;
    }
}

extern "C" {
    // 供 Go 调用的 C 包装函数
    // 负责显存分配、数据拷贝和内核启动
    void runMatrixMul(float *h_a, float *h_b, float *h_c, int width) {
        int size = width * width * sizeof(float);
        float *d_a, *d_b, *d_c;

        // 1. 分配 GPU 显存 (Device Memory)
        cudaMalloc((void **)&d_a, size);
        cudaMalloc((void **)&d_b, size);
        cudaMalloc((void **)&d_c, size);

        // 2. 将数据从 Host (CPU内存) 复制到 Device (GPU显存)
        // 这一步通常是性能瓶颈,应尽量减少
        cudaMemcpy(d_a, h_a, size, cudaMemcpyHostToDevice);
        cudaMemcpy(d_b, h_b, size, cudaMemcpyHostToDevice);

        // 3. 定义 Grid 和 Block 维度
        // 每个 Block 包含 16x16 = 256 个线程
        dim3 threadsPerBlock(16, 16);
        // Grid 包含足够多的 Block 以覆盖整个矩阵
        dim3 numBlocks((width + threadsPerBlock.x - 1) / threadsPerBlock.x,
                       (width + threadsPerBlock.y - 1) / threadsPerBlock.y);

        // 4. 启动内核!成千上万个线程开始并行计算
        matrixMulKernel<<<numBlocks, threadsPerBlock>>>(d_a, d_b, d_c, width);

        // 5. 将计算结果从 Device 传回 Host
        cudaMemcpy(h_c, d_c, size, cudaMemcpyDeviceToHost);

        // 6. 释放 GPU 内存
        cudaFree(d_a);
        cudaFree(d_b);
        cudaFree(d_c);
    }
}

3. 定义 C 头文件 (matrix.h)

// matrix.h
#ifndef MATRIX_H
#define MATRIX_H

void runMatrixMul(float *a, float *b, float *c, int width);

#endif

4. 编写 Go 主程序 (main.go)

在 Go 代码中,我们准备数据,并通过 CGO 调用 runMatrixMul。

// go-cuda-cgo-demo/main.go
package main

/*
#cgo LDFLAGS: -L. -lmatrix -L/usr/local/cuda/lib64 -lcudart
#include "matrix.h"
*/
import "C"
import (
    "fmt"
    "math/rand"
    "time"
    "unsafe"
)

const width = 1024 // 矩阵大小 1024x1024,共 100万次计算

func main() {
    size := width * width
    h_a := make([]float32, size)
    h_b := make([]float32, size)
    h_c := make([]float32, size)

    // 初始化矩阵数据
    rand.Seed(time.Now().UnixNano())
    for i := 0; i < size; i++ {
        h_a[i] = rand.Float32()
        h_b[i] = rand.Float32()
    }

    fmt.Printf("Starting Matrix Multiplication (%dx%d) on GPU...\n", width, width)
    start := time.Now()

    // 调用 CUDA 函数
    // 使用 unsafe.Pointer 获取切片的底层数组指针,传递给 C
    C.runMatrixMul(
        (*C.float)(unsafe.Pointer(&h_a[0])),
        (*C.float)(unsafe.Pointer(&h_b[0])),
        (*C.float)(unsafe.Pointer(&h_c[0])),
        C.int(width),
    )

    // 注意:在更复杂的场景中,需要使用 runtime.KeepAlive(h_a)
    // 来确保 Go GC 不会在 CGO 调用期间回收切片内存。

    elapsed := time.Since(start)
    fmt.Printf("Done. Time elapsed: %v\n", elapsed)

    // 简单验证:检查左上角元素
    fmt.Printf("Result[0][0] = %f\n", h_c[0])
}

5. 编译与运行

前提:确保你的机器安装了 NVIDIA Driver 和 CUDA Toolkit。nvcc是CUDA编译器工具链,可以将基于CUDA的代码翻译为GPU机器码。

步骤一:编译 CUDA 代码

nvcc -c matrix.cu -o matrix.o
ar rcs libmatrix.a matrix.o

步骤二:编译 Go 程序

# 链接本地的 libmatrix.a 和系统的 CUDA 运行时库
go build -o gpu-cgo-demo main.go

步骤三:运行

./gpu-cgo-demo

预期输出:

Starting Matrix Multiplication (1024x1024) on GPU...
Done. Time elapsed: 611.815451ms
Result[0][0] = 262.440918

性能优化——从能用到极致

代码跑通只是第一步。Sam 推荐使用 NVIDIA 的 Nsight Systems (nsys) 来进行性能分析。你会发现,虽然 GPU 计算极快,但PCIe 总线的数据传输往往是最大的瓶颈

优化黄金法则:

  1. 减少传输:PCIe 很慢。尽量一次性将所有数据传给 GPU,让其进行多次计算,最后再取回结果。
  2. 利用共享内存 (Shared Memory):Block 内的共享内存比全局显存快得多。在矩阵乘法中,可以利用它实现分块算法 (Tiling),将小块矩阵加载到共享内存中复用,从而大幅减少显存带宽压力。

小结:Gopher 的新武器

Go + CUDA 的组合,为 Go 语言打开了一扇通往高性能计算的大门。它证明了 Go 不仅是编写微服务的利器,同样可以成为驾驭底层硬件、构建计算密集型应用的强大工具。如果你正在处理大规模数据,不妨尝试将计算任务卸载给 GPU,你会发现,那个熟悉的蓝色 Gopher,也能拥有令人惊叹的爆发力。

资料链接:

  • https://www.youtube.com/watch?v=d1R8BS-ccNk
  • https://sam-burns.com/posts/gophercon-25-go-faster/#gophercon-2025-new-york

本文涉及的示例源码可以在这里下载。

附录:告别 CGO?尝试 PureGo 的无缝集成

虽然 CGO 是连接 Go 和 C/C++ 的标准桥梁,但它也带来了编译速度变慢、工具链依赖等问题。有没有一种更“纯粹”的 Go 方式?

答案是有的。借助 PureGo 库,我们可以在不开启 CGO 的情况下,直接加载动态链接库 (.so / .dll) 并调用其中的符号。

让我们看看如何用 PureGo 重写上面的 main.go。

1. 准备动态库

首先,我们需要将 CUDA 代码编译为共享对象 (.so),而不是静态库。

# 编译为共享库 libmatrix.so
nvcc -shared -Xcompiler -fPIC matrix.cu -o libmatrix.so

2. 编写 PureGo 版主程序 (go-cuda-purego-demo/main.go)

// go-cuda-purego-demo/main.go
package main

import (
    "fmt"
    "math/rand"
    "runtime"
    "time"

    "github.com/ebitengine/purego"
)

const width = 1024

func main() {
    // 1. 加载动态库
    // 注意:在运行时,libmatrix.so 和 libcuder.so 必须在 LD_LIBRARY_PATH 中
    libMatrix, err := purego.Dlopen("libmatrix.so", purego.RTLD_NOW|purego.RTLD_GLOBAL)
    if err != nil {
        panic(err)
    }

    // 还需要加载 CUDA 运行时库,因为 libmatrix 依赖它
    _, err = purego.Dlopen("/usr/local/cuda/lib64/libcudart.so", purego.RTLD_NOW|purego.RTLD_GLOBAL)
    if err != nil {
        panic(err)
    }

    // 2. 注册 C 函数符号
    var runMatrixMul func(a, b, c *float32, w int)
    purego.RegisterLibFunc(&runMatrixMul, libMatrix, "runMatrixMul")

    // 3. 准备数据 (与 CGO 版本相同)
    size := width * width
    h_a := make([]float32, size)
    h_b := make([]float32, size)
    h_c := make([]float32, size)

    rand.Seed(time.Now().UnixNano())
    for i := 0; i < size; i++ {
        h_a[i] = rand.Float32()
        h_b[i] = rand.Float32()
    }

    fmt.Println("Starting Matrix Multiplication via PureGo...")
    start := time.Now()

    // 4. 直接调用!无需 CGO 类型转换
    runMatrixMul(&h_a[0], &h_b[0], &h_c[0], width)

    // 5. 极其重要:保持内存存活
    // PureGo 调用是纯汇编实现,Go GC 无法感知堆栈上的指针引用
    // 必须显式保活,否则在计算期间 h_a 等可能被 GC 回收!
    runtime.KeepAlive(h_a)
    runtime.KeepAlive(h_b)
    runtime.KeepAlive(h_c)

    fmt.Printf("Done. Time: %v\n", time.Since(start))
    fmt.Printf("Result[0][0] = %f\n", h_c[0])
}

3. 运行

# 无需 CGO,直接在go-cuda-purego-demo下运行
# 确保当前目录在 LD_LIBRARY_PATH 中
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
CGO_ENABLED=0 go run main.go
Starting Matrix Multiplication via PureGo...
Done. Time: 584.397195ms
Result[0][0] = 260.088806

优势

  • 编译飞快:没有 CGO 的编译开销。
  • 零外部依赖:编译环境不需要安装 GCC 或 CUDA Toolkit,只要运行时环境有 .so 即可。这对于在轻量级 CI/CD 环境中构建分发包非常有用。

注意:PureGo 方案虽然优雅,但也失去了 CGO 的部分类型安全检查,且需要开发者更小心地管理内存生命周期 (runtime.KeepAlive)。


你的“算力”狂想

Go + GPU 的组合,打破了我们对 Go 应用场景的想象边界。在你的业务场景中,有没有哪些计算密集型的任务(比如图像处理、复杂推荐算法、密码学计算)是目前 CPU 跑不动的?你是否会考虑用这种“混合动力”方案来重构它?

欢迎在评论区分享你的脑洞或实战计划! 让我们一起探索 Go 的算力极限。

如果这篇文章为你打开了高性能计算的大门,别忘了点个【赞】和【在看】,并转发给那个天天喊着“CPU 跑满了”的同事!


还在为“复制粘贴喂AI”而烦恼?我的新专栏 AI原生开发工作流实战 将带你:

  • 告别低效,重塑开发范式
  • 驾驭AI Agent(Claude Code),实现工作流自动化
  • 从“AI使用者”进化为规范驱动开发的“工作流指挥家”

扫描下方二维码,开启你的AI原生开发之旅。


你的Go技能,是否也卡在了“熟练”到“精通”的瓶颈期?

  • 想写出更地道、更健壮的Go代码,却总在细节上踩坑?
  • 渴望提升软件设计能力,驾驭复杂Go项目却缺乏章法?
  • 想打造生产级的Go服务,却在工程化实践中屡屡受挫?

继《Go语言第一课》后,我的《Go语言进阶课》终于在极客时间与大家见面了!

我的全新极客时间专栏 《Tony Bai·Go语言进阶课》就是为这样的你量身打造!30+讲硬核内容,带你夯实语法认知,提升设计思维,锻造工程实践能力,更有实战项目串讲。

目标只有一个:助你完成从“Go熟练工”到“Go专家”的蜕变! 现在就加入,让你的Go技能再上一个新台阶!


商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。

© 2026, bigwhite. 版权所有.

🔲 ☆

【译文】Grid 用于布局, Flexbox 用于组件

本文是 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 提供反馈!

感谢你的阅读!

❌