为什么从其他转前端会觉得前端很难?也许是因为各种奇怪的 CSS Hacks 吧。

0. 动效残影与图层

给一个 <td>:hover 伪类随便写点 transition 过渡就可能遇到这样的情况:

残影

但是,如果同时加了一点 3D 效果,比如 translateZ(0),问题就又消失了。

如果看一遍 DevTools 的性能面板,就能知道区别在哪:

未加 3D 前
加了 3D 后

没有 3D 效果时,重绘 (repaint) 的图层 (layer) 是整个文档;而有 3D 效果时,这个节点会被独立成一个图层进行渲染。

打开 DevTools - More tools - Layers 或是在 Rendering 工具里勾选 Layer Borders 即可验证。

当然,translateZ(0) 会有一些副作用,所以一般会用 backface-visibility: hidden 这个本来用来控制 3D 元素背部可见性的属性来达到同样的效果。

1. 消失的底边

但是这带来了一个问题,底边没了:

底边没了

如果给底边单独设双倍的高度,则可以正常显示。那么问题很明显,就是边框高度溢出了图层。

但是,这问题没有一致性,几乎一致的几个表格,不一定哪个会出现。

不过,打开 Layer Borders 可以发现一个规律:

图层紧贴的话反而能正常显示

回想之前的动效残留,可以发现这其实是同一问题。那么最简单的解决方案就是:

增大图层范围。这里增加一个 box-shadow 来撑开图层。

box-shadow

这时,如果把之前加的 backface-visibility: hidden 去掉,会发现它已经不再被需要了。

2. 「要开始变形了」

但是「众所周知」的是,box-shadow 会吃渲染性能的(尤其是没有优化的低版本浏览器)。

而如果去掉强制独立图层的话,整页渲染加上 box-shadow 无疑会降低性能。

这时候就要 will-change 属性,预先告诉浏览器,该元素将会对哪个属性添加动效。

浏览器支持方面,Chrome 和 Firefox 版本均为 36+。

3. 避免重绘

即便预告了 box-shadow,但优化还是很有限,因为毕竟还有重绘的过程。

那么 box-shadow 可以不重绘吗?

::after 伪元素画上盒阴影,然后用 opacity 隐藏掉,待需要时再显示。

没有重绘

opacity 改变的元素,会被单独放在一个图层,而且透明度改变的性能成本较低。

伪元素的图层

不过,这样就没法用来解决第一个问题了。

所以结论就是,<table> 已经没人要用了 ≡(▔﹏▔)≡


本文参考了:

[1] https://stackoverflow.com/questions/28511539/the-underlying-magic-of-webkit-backface-visibility

[2] https://www.barretlee.com/blog/2015/10/14/a-incredible-bug-in-taobao-homepage/

[3] https://www.zhangxinxu.com/wordpress/2015/11/css3-will-change-improve-paint/

[4] https://juejin.cn/post/6844903584077889544

[5] https://segmentfault.com/a/1190000011337088