做前端的,谁没被CSS坑过?

但是,这次的坑,真的是让我印象深刻。一个CSS Grid布局的Bug,我从晚上十点,排查到第二天早上六点,整整一夜,才找到原因。

今天,把这次排查过程记录下来,既是给自己留个纪念,也希望能给大家带来一些启发。

一、问题出现

那天晚上十点,我正准备睡觉,突然,产品经理在群里@我,说线上有个页面,在某些用户的电脑上,布局错乱了。

我赶紧打开电脑,登录线上环境,看了一下。那个页面,是我用CSS Grid做的一个卡片布局,正常情况下,应该是三列,每行三个卡片,整齐排列。但是,在某些用户的电脑上,卡片都挤到了左边,一列排下来,很难看。

我先在自己的电脑上看了一下,Chrome浏览器,最新版本,布局是正常的。然后,我又试了Firefox和Safari,也都正常。

那问题出在哪呢?

我问产品经理,那些出问题的用户,用的是什么浏览器。产品经理说,大部分是Chrome,但是,版本好像比较旧。

我心里咯噔一下。难道是浏览器兼容性问题?

但是,CSS Grid在Chrome 57以上就支持了,现在都2018年了,Chrome都出到67了,谁还用那么旧的版本?

我让产品经理,找一个出问题的用户,问一下具体的浏览器版本。过了一会儿,产品经理回复说,那个用户用的是Chrome 58。

Chrome 58,确实支持CSS Grid啊。我在自己的电脑上,用Chrome 58的模拟器试了一下,布局也是正常的。

这就奇怪了。同样是Chrome 58,为什么有的电脑正常,有的电脑不正常?

二、开始排查

我决定,深入排查一下。

首先,我怀疑是缓存问题。有时候,浏览器缓存了旧的CSS文件,会导致布局错乱。我让那个用户,清一下缓存,硬刷新一下。但是,用户说,清了缓存,还是不行。

然后,我怀疑是用户的电脑,分辨率或者缩放比例的问题。我问用户,电脑的分辨率是多少,浏览器缩放是多少。用户说,分辨率是1920x1080,缩放是100%。这和我的电脑一样,应该不是这个问题。

接着,我怀疑是用户安装了某些浏览器插件,干扰了页面布局。我让用户,禁用所有插件,再试一下。用户说,禁用了,还是不行。

这时候,已经晚上十一点了。我有点着急了。

我决定,远程连接用户的电脑,亲自看一下。通过远程桌面,我看到了用户的电脑。果然,布局是错乱的。卡片都挤到了左边,一列排下来。

我打开开发者工具,看了一下Elements面板。CSS规则都加载了,grid-template-columns也设置了,grid-gap也设置了,看起来都没问题。但是,布局就是不对。

我又看了一下Computed面板,发现了一个奇怪的现象。grid-template-columns的计算值,不是我设置的repeat(3, 1fr),而是none。

为什么会是none?我明明设置了啊。

我仔细看了一下CSS规则,发现了问题。我的CSS是这样写的:

.card-container {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-areas: "card1 card2 card3"
                       "card4 card5 card6";
  grid-gap: 20px;
}

看起来没问题啊。但是,为什么grid-template-columns的计算值是none?

我试着,在开发者工具里,把grid-template-areas那一行注释掉。奇迹发生了,布局正常了!

原来,问题出在grid-template-areas上。

三、深入分析

找到问题所在了,但是,为什么grid-template-areas会导致grid-template-columns失效呢?

我仔细看了一下我的grid-template-areas的定义:

grid-template-areas: "card1 card2 card3"
                     "card4 card5 card6";

看起来没问题啊。两行,每行三个区域,和grid-template-columns的三列对应。

但是,我突然想到,我的卡片,是动态生成的,数量不固定。有时候是6个,有时候是9个,有时候更多。而我的grid-template-areas,只定义了两行6个区域。如果卡片数量超过6个,多余的卡片,就没有对应的area了。

但是,这应该不会导致布局错乱啊。多余的卡片,应该会自动放到新的行里,按照grid-template-columns的定义,三列排列。

为什么在Chrome 58上,就不行了呢?

我去查了一下CSS Grid的规范。grid-template-areas属性,定义了网格的区域。如果grid-template-areas定义了N行M列,那么,网格就有N行M列。如果grid-template-columns也定义了,但是列数和grid-template-areas的列数不一致,会怎么样?

规范里说,如果grid-template-columns和grid-template-areas同时定义,那么,列数以grid-template-areas为准。如果grid-template-columns定义的列数,和grid-template-areas的列数不一致,那么,grid-template-columns会被忽略。

哦!原来如此!

我的grid-template-areas,定义了两行三列。但是,我的卡片数量不固定,有时候超过6个。当卡片数量超过6个时,多余的卡片,会自动创建新的行。但是,这些新的行,没有在grid-template-areas中定义,所以,它们的列数,是由grid-auto-columns决定的,而不是grid-template-columns。

而我,没有设置grid-auto-columns。所以,默认值是auto。这就导致,多余的卡片,列宽是auto,而不是1fr,所以,都挤到了左边。

但是,为什么在新版Chrome上,就正常呢?

我又查了一下,发现,新版Chrome,对CSS Grid的实现,做了优化。当grid-template-areas定义的行不够时,新创建的行,会沿用grid-template-columns的定义。而旧版Chrome,严格按照规范,新行的列宽由grid-auto-columns决定。

所以,这就是为什么,新版Chrome正常,旧版Chrome不正常。

四、解决方案

找到原因了,解决方案就简单了。

有两种解决方案:

方案一:去掉grid-template-areas

既然我的卡片是动态生成的,数量不固定,那就不需要用grid-template-areas。直接用grid-template-columns定义列数,卡片会自动排列。

.card-container {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-gap: 20px;
}

这样,不管有多少个卡片,都会三列排列,不会出问题。

方案二:设置grid-auto-columns

如果一定要用grid-template-areas,那就同时设置grid-auto-columns,让自动创建的行,也有正确的列宽。

.card-container {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-columns: 1fr;
  grid-template-areas: "card1 card2 card3"
                       "card4 card5 card6";
  grid-gap: 20px;
}

这样,自动创建的行,列宽也是1fr,布局就正常了。

我选择了方案一,因为,对于动态生成的卡片,grid-template-areas确实没什么用,反而增加了复杂度。

改完代码,部署上线,让那个用户再试一下。用户说,布局正常了。

这时候,已经是第二天早上六点了。窗外,天已经亮了。

五、踩过的坑

这次排查,踩了不少坑,总结一下:

1. 不要只在最新版浏览器测试

现在,很多前端开发者,只在最新版Chrome上测试,觉得其他浏览器都不用管了。但是,实际上,还有很多用户,在用旧版浏览器。特别是企业用户,很多还在用IE,或者旧版Chrome。

所以,兼容性测试,一定要做。至少,要覆盖主流浏览器的几个主要版本。

2. CSS Grid的坑,比你想象的多

CSS Grid很强大,但是,也有很多坑。特别是不同浏览器的实现差异,有时候,会让你怀疑人生。

比如,这次的grid-template-areas和grid-template-columns的优先级问题,规范里写了,但是,很多人不知道。而且,不同浏览器的实现,还有差异。

所以,用CSS Grid的时候,一定要多测试,多查规范,不要想当然。

3. 远程调试很重要

这次排查,如果不是远程连接用户的电脑,亲自看了一下,我可能永远也找不到原因。因为,在我的电脑上,一切正常。

很多时候,线上问题,是和用户的环境相关的。只在自己的电脑上排查,是不够的。一定要想办法,复现用户的环境,或者直接在用户的环境上调试。

4. 不要忽视CSS的问题

很多前端开发者,觉得CSS很简单,不就是写写样式吗?但是,实际上,CSS的水很深。特别是布局,涉及到盒模型、定位、浮动、Flexbox、Grid等,各种属性相互作用,很容易出问题。

而且,CSS的问题,往往很难排查。因为,它不会报错,只会显示不对。你需要一点点地试,一点点地排除,才能找到原因。

所以,不要轻视CSS,要认真学习,深入理解。

六、写在最后

这次的Bug,排查了一夜,虽然很累,但是,也学到了很多东西。

CSS Grid,是一个很强大的布局工具,但是,也有很多细节需要注意。特别是在动态内容的场景下,grid-template-areas的使用,要格外小心。

而且,浏览器兼容性,永远是前端开发者绕不开的话题。不要以为,大家都在用最新版浏览器,就不用管兼容性了。实际上,还有很多用户,在用旧版浏览器。

最后,想说一句,做前端,不容易。但是,每次解决了一个难题,那种成就感,也是无法替代的。

希望这篇文章,能给大家带来一些启发。如果你也遇到过类似的CSS Grid的坑,欢迎在评论区留言,我们一起交流。