IE浏览器中,border-radius和overflow:hidden同时使用页面卡死问题

394 阅读4分钟

背景

最近项目中遇到一个性能问题,页面中仅仅展示了一个table,没有分页,大约有二十几列,四十几行,因为要实现表头固定、内容滚动以及和其它分页列表一样的样式,因此统一使用了公司自研的一个前端ui组件库(类似于elementUI)。页面在chrome下面运行正常,在IE下出现页面不断自动刷新重复渲染直至浏览器崩溃的情况

问题排查

  • 渲染节点过多的问题

    首先考虑到是否为页面渲染dom元素过多导致的页面卡顿,虽然心知就这点元素导致页面直接卡死的可能性微乎其微,但是还是尝试了一下,将列表数据只保留一条,果不其然,没有任何效果

  • 组件库使用导致内存泄漏问题

    紧接着我就想到,会不会是公司自研组件库的使用造成了内存泄漏了问题,因为此前有项目就是因为大量使用了组件库中的组件导致内存泄漏,而且当时排查问题的时候做了测试,渲染10000个div和渲染10000个实际渲染结果也是div的组件,内存占用相差很大,因此考虑将组件给替换掉,直接使用table

  • 使用table替换ui组件

    • 首先直接尝试了将组件渲染后的dom直接复制到页面上,将原有的组件直接替换为渲染后的dom,尝试启动页面,依旧卡顿。看来并不是组件封装或者使用了组件库的问题
    • 接着尝试简化代码,首先尝试将本次需求不需用到的dom元素全部删掉,尝试后还是没有解决问题。然后尝试将table自己重写,但保留了页面外层布局容器(因为表头固定是要两个table一个写head,一个写body的,这样内部滚动条才不会有问题,保留外层容器是为了保留这个功能),结果依旧以页面卡死告终
    • 最后放弃所有原有代码,全部自己用table重新实现,成功了,IE下面运行流畅,一点卡顿都没有了
  • 为什么会出现这么大的差别呢?

    回想之前作出的所有尝试,最终不卡顿的页面和之前尝试的卡顿版本有什么区别呢?首先页面元素是差不太多了,其次都不涉及js的内容,那么有问题的就只有css了,因为之前为了快速验证,重写的替代页面是没有包含任何css的,而且当时我是替换掉了两个table的内容只保留了外层容器,那么能够造成影响的就只能是外层容器的样式了,想到这里,我立即将代码还原为最初状态,然后删掉了外层容器的样式,果不其然,页面运行流畅。外层容器样式不多,通过穷举,发现删掉border-radius属性后,就能解决问题

问题思考与结论

虽然问题已经得到解决,但是为什么会出现这样的问题呢,作为一个程序员如果留下这样的疑问不解决那肯定是浑身难受,而且有了解决方案之后,对于问题搜索的关键字定位就容易了很多。最终通过查阅资料得出结论

  • 该问题并非单纯的border-radius属性造成的,而是当border-radius和overflow:hidden同时作用于一个元素上是,才会出现这种页面卡死的情况
  • 该问题仅仅出现在IE(所有版本)浏览器上,其它浏览器没有该问题
  • 这也许是因为IE为了做一个性能方面的优化而产生的新问题(原作者也表明没有证实这个结论,仅作为合理的猜测)
    • 在页面渲染的过程中,一些本该隐藏的元素或者部分正在渲染,导致页面渲染速度减慢
    • IE为了解决这个问题,在重绘和回流等事件期间,它首先循环遍历所有这些元素,并检查它们是否完全超出范围,这个速度很快,只需要几毫秒。但是此时问题就出现了,当溢出隐藏的元素不是矩形(例如添加了border-radius),那么这个是否超出边界的计算会复杂得多,它需要根据svg路径的实际形状进行比较,不再是单纯的比较一个矩形,正是因为这个复杂的计算,导致页面渲染性能大大降低

参考资料

How does overflow: hidden; & border-radius on a container cause massive slowdowns to "Paint / Render Layer" within container, only on IE?