Webnovel 不用照顾 Edge 浏览器性能?想多了!

本文做者:任家乐html

原创声明:本文为阅文前端团队 YFE 成员出品,请尊重原创,转载请联系公众号 ( id: yuewen_YFE ) 获取受权,并注明做者、出处和连接。前端

前言

曾写过一篇性能优化 “ 长篇报告 ” 「 checkbox 美化引起的蝴蝶效应 」 ,也曾感叹 CSS 对渲染的影响是如此大,也许深化记忆点的代价就是被同一块石头绊倒2次 ?是的,性能优化“报告”第二弹来了,但愿本篇文章能够在优化页面性能上给你们提供一些思路。web

看点

某些 CSS 属性或 JS 操做,看似简单却易隐藏陷阱?chrome

  • visibility - 在不改变文档布局的状况下展现或隐藏某个元素。
  • clientHeight / clientWidth - 获取元素内部的宽高。

磨刀不误砍柴工,性能工具的亲密接触老是必不可少的。浏览器

  • Edge 浏览器性能工具怎么用
  • 如何快速的定位、解决性能问题?

同是浏览器,渲染层面的表现却如此不一样?性能优化

  • Edge 浏览器的渲染策略和 Chrome 有何不一样?

性能炸弹

问题始于一个 “ 红色性能炸弹 ” ,瞄准的是 Webnovel「起点海外版」 PC 站点阅读页,关键字:Edge 浏览器、CPU、RAM。Webnovel 的国外用户指出,在 Edge 浏览器下,阅读页 CPU 消耗的很是大,他的腿甚至感觉到了笔记本电脑的灼烧。异步

Webnovel 阅读页,为了更趋近于完美的阅读体验,咱们作了:预加载上下章、支持上下键切换章节、无刷新跳章节等优化,在阅读过程当中,你甚至不会看到内容加载过程当中的 loading 。然而面对此次的性能突击,这些优化在 Edge 浏览器下显得如此眇小...而对因而否值得作这次性能优化,Google Analytics 则给出了答案:chrome-devtools

「 Webnovel 浏览器占比一览」工具

Edge 浏览器 TOP4 (2.53%)的占比状况已不容忽略!看来注定免不了和 Edge 的一场针锋相对了,立马去 Edge 浏览器上一探究竟~布局

问题的剖析

定位问题以前,心里有个声音在提醒我:是 Google 广告脚本的问题!由于前不久阅读页引入了 Google 广告,而且针对广告的展现及隐藏作过一些逻辑的调整,带着目的首先从 JS 开始分析。

性能工具中寻找问题代码 - Google 脚本在不断获取 clientHeight、clientWidth?

我是用 Edge 浏览器自带的性能工具来进行分析的,因为 Edge 只能在 win10 系统上运行,我工做中用的是 iMac 因此几乎没有使用过 ,也只能回家 “踢走” 老公倒腾他专门打游戏的台式机了,其 Edge 浏览器版本:42.17134.1.0。

打开 Edge 开发者工具,性能工具的入口映入眼帘。

「 Edge 开发者工具 」

切入正题:「 访问 Webnovel 阅读页 - 打开“ 性能 ” 标签栏 - 按下运行按钮 」。为了还原用户的使用场景,我也认真的滚动浏览了十几章。运行的结果:

「 首次性能工具的结果图 」

这满屏的原谅色,看到这张图,答应我请第一眼就确认是 “渲染” 的问题好吗?请立马注释 CSS 一探究竟好吗?但我并无作到。

继续观察,发现性能工具中有2个子标签栏: 时间线、JavaScript 调用堆栈。调用堆栈里很清楚的指明了 CPU 占用率最高的2项(下图),果真出自于 Google 广告的 osd.js 脚本。

「 首次性能工具结果图之 JavaScript 调用堆栈 」

不能慌,证据还不够确凿。Edge 的性能工具其实能够精肯定位到 JS 中的问题代码片断,在红框区域点击鼠标右键,也能够定位到问题源码,这一点 very good!感受离事实的真相更近了一步。

「 追溯代码问题细节图 」

「 问题代码片断1 」

「 问题代码片断2 」

显而易见,Google osd.js 脚本设置了计时器,不停地获取 HTML 节点的 clientWidth 和 clientHeight,因此祸根源于此?

clientHeight、clientWidth 的一些隐患

Facebook 的工程师 Stoyan 在他的博客中曾提过, 浏览器是聪明的,为了节约资源、不频繁触发 Repaint(重绘)、Reflow(重排),它会将一系列脚本中须要执行的 UI 改动放在一个队列中一块儿执行,而不是每一个改动都单独执行从而引起无数次的 Repaint、Reflow。BUT!有一些操做会打乱它原有的 plan 和秩序:

  1. offsetWidth,offsetHeight,offsetTop,offsetLeft
  2. scrollTop, scrollLeft,scrollWidth, scrollHeight
  3. clientlTop, clientLeft,clientWidth, clientHeight
  4. getComputedStyle(),or currentStyle in IE

浏览器对于这些操做,会“ 很是及时地 ”给予最准确实时的答案,所以会触发 Reflow,例如从新计算样式、更新图层等。要知道 Reflow 相比 Repaint 对渲染的影响更大、损耗更多。

demo time - 获取 clientWidth 是否会触发没必要要的渲染?

功能简述:

  1. 页面 onload 后设置计时器每隔 4ms(浏览器默认最小时间间隔) 获取 HTML 节点的clientWidth。
  2. 点击 click me 按钮水平位移 box1 到最右侧。
  3. 点击 no timer 按钮清除计时器,同时水平位移 box1 到原位。

「 demo 动图 」

过程

结果

阶段一:before “ click me ”

未触发 Reflow 等渲染事件

阶段二:after “ click me ”

触发了额外的渲染(recalculate style、update layers)

阶段三:after “ no timer ”

没有触发 Reflow 等渲染事件

Chrome Performance 工具则更直观地描述了表格中的结果(红框中处于阶段二,清楚表现了额外的渲染):

为了更突出差别,我将水平移动的 div 数量增长到了 20 个,Recalculate Style 的数据相差了 70ms:

「 渲染数据的差别:动画期间获取 clientWidth(上),动画期间不作任何操做(下) 」

demo 的结果验证了在 Chrome 浏览器中,动画期间获取 clientWidth / clientHeight 确实会引起浏览器的 Reflow,随着动画复杂度的增长以及元素的增长,该影响只会更大,进而增长了没必要要的渲染消耗。

首次尝试 - 删除 Google 广告

既然阅读页 CPU 飙升极有多是由于 Google 的 osd.js 不断获取 HTML 节点的 clientWidth / clientHeight 意外触发了屡次 Reflow ,那么解决问题的第一步便是删除 Google 广告。我利用 fiddler 代理 JS 脚本到本地,删除了广告相关的代码。

但其结果尽和个人猜想彻底不一样,删除广告并无使页面性能变得更好,有点没有头绪...真的不是 Google 广告的问题?

再次试探 - Google 广告在其余站点有无相似问题

继续用 Edge 性能工具对一相似网站进行测试,由于这个网站一样使用了 Google 广告,而且相比于 Webnovel 阅读页首次初始化1枚广告,它的首页尽然同时加载了 4 枚广告。然而结果却没有渲染上的问题(以下图),网站 CPU 利用率一栏甚至彻底没有飚绿的痕迹。

推翻设想,转换方向到 CSS

太过坚信设想反而偏离正轨?前期我太坚信是 Google 广告脚本的问题,既然以上 2 次的尝试都证实不是 Google 广告脚本的锅,那么是时候转换方向了。

曾经写的「 checkbox 美化引起的蝴蝶效应 」带给了我一些灵感:CSS 在渲染性能上有着相比 JS 更不容忽视的影响力。结合前面提到的 demo 结论,不禁得猜想也许阅读页的性能问题是因为某些 CSS 属性或动画与 Google 广告脚本中的 clientHeight、clientWidth 产生了相互做用,引起了额外的渲染。此时尝试注释全部样式,最终绿色居然消失了,这说明极有多是 CSS 的锅。

「 CPU 占用率中消失的渲染 」

罪魁祸首是 visibility : hidden ?

继续从性能工具中寻找更精确的答案,意外发现 Edge 性能工具里的 “ 时间线 ” 标签栏里能够捕捉到不少有用的信息,甚至能够找到样式相关的条目?展开便可定位到目前引起渲染的 DOM,这一点太赞了!

这对问题的定位有了很大的帮助,由于我很清楚地看到了有问题的 DOM ,也就是咱们写的 loading 组件。

Webnovel 阅读页确实存在几枚隐藏的 loading,分别存在于章评弹窗中、目录弹窗中、各章节内容之间。

「 章评弹窗、目录弹窗 」

「 章节之间的 loading 」

隐藏 loading ,是为了简化逻辑,将其“ 埋伏隐藏 ” 在各自须要的地方,在获取所需的异步数据以前,能够第一时间将 loading 展现给用户,获取到须要的数据后再将 loading 隐藏,这样的交互逻辑是合理的,那么隐藏的 loading 为何会对渲染产生影响?Webnovel 阅读页弹窗中的 loading 是用 visibility: hidden 来隐藏的,这样的方式不对?

visibility: hidden 有利有弊

看不见 loading 的踪影,它却依然能对渲染产生这么大的影响?你认为正确地隐藏了其中的 loading,而 loading 却实际地存在于文档流中,它的每个动画,都会影响同在文档流中的其余节点。

  • visibility: hidden // DOM 的渲染不会忽略其全部节点
  • display: none // DOM 的渲染忽略其全部节点

如此来看,性能更优的应该是 display: none,不在文档流中就不会影响其余的元素。但每一个被创造出来的 CSS 属性必然有创造它的道理,若是 display: none 是最推荐的方式,为何还会有 visibility : hidden 的存在?任何事物都存在两面性,例如如下场景中,visibility: hidden 竟协助某大神解决了性能问题。

Google 的工程师 Jake Archibald 在他的博客「Solving rendering performance puzzles」中,用一个有趣的 SVG 动画验证了 Layout (布局)在网页性能中举足轻重的地位。随着动画中 SVG 文字的不断变化,动画的帧率由原来最优的 60fps 最终下降到不足于 10fps。Chrome 的 Performance 工具揭示了其主要因素是产生了大量的 Layout 消耗(称为 Layout Thrashing or Reflow )。

此时 Jake 利用 visibility: hidden 解决了 Layout 消耗过多的问题,他提到,visibility: hidden 的元素,由于其在文档流中占据一席之地,于是将其设置为 visibility: visible 并不会产生 Layout 上的消耗,只会产生额外的 paint,借助此想法,他事先将 SVG 中的全部字符分别放置于 <textpath> 节点中的子节点 <tspan> 中,并利用 visibility: hidden 进行隐藏:

「 DOM 中隐藏的 SVG 文字 」

随后逐次将 <tspan> 的 visibility 属性值变动为 visible ,字符就依次展现了出来,对比下,他原来的作法就真的是很是粗暴了,即:将全部字符存于单独的一个节点中,每次截取固定长度的文字设置为 <textPath> 的值。大量的重置 textpath 节点值最终引起了浏览器每一个字符的从新布局( layout )。

所以并不能笼统地下定论 visibility: hidden 不如、或优于 display: none ,只能说在不一样的场景下他们都有各自的优点。

问题的修复

显然,loading 动画用 visibility: hidden 进行隐藏确实产生了不少额外的渲染消耗。修复这个问题的方案不少,例如,能够将 loading 的隐藏方式改成 display: none,或者是在打开弹窗的时候将 loading 实时添加到 DOM 中,最终为避免改动弹窗组件的 CSS 产生的全局影响,咱们在打开弹窗的时候添加了 loading 的 DOM 节点,关闭弹窗时则将其移除。一周后用户的反馈也证实了此方案是有效的(下图)。

「 用户的反馈 」

新方案的尝试

Edge 浏览器渲染上和 Chrome 有什么不一样 ?

Webnovel 阅读页的问题虽然得以解决,但可能咱们都会感到疑惑,为何这样的性能问题在 Chrome 浏览器上并无发生,或者说表现不明显?这里我借助「 visibility: hidden 有利有弊」一节中提到的 Jake 的 SVG 动画作了一波测试。性能工具简单探测一下,发现未优化前的动画其性能和 Chrome 同样没法直视:

「 Edge 浏览器性能结果 」

优化后的动画其表现和 Chrome 也相差甚远,直观上的对好比动图所示,Edge 浏览器(第二张图)上的动画比 Chrome(第一张图) 缓慢许多。

「 上 Chrome, 下 Edge 」

Chrome Performance 工具中能够证实 Jake 的优化策略确实将 Layout 产生的消耗下降了不少,由原来的 TOP3 变为最后一位 :

「 优化前,优化后 」

但 Edge 性能工具的结果甚至比不上优化以前,渲染上未下降,而且出现了不少 JS 上的 CPU 冲击(图中橘色区域):

「 优化前 」

「 优化后 」

优化后渲染层面的开销甚至大于优化前的开销(在动画运行差很少时长的状况下):

「 优化前、后 UI线程数据 」

在此 demo 的表现上,Edge 浏览器对 SVG 动画的渲染状况与 Chrome 截然相反,更重要的是,Chrome 对于 Layout(布局)和 Paint (重绘) 在渲染性能方面的 “ 权衡 ”彷佛和 Edge 是不同的,下降大量的 Layout 开销确实可使 Chrome 中的动画更为流畅,但这在 Edge 中却行不通,甚至表现相反。

解决问题的新思路

Chrome 和 Edge 浏览器在 SVG 动画上的表现差别提供了我一些新的思路,再次回到 Webnovel 阅读页,咱们的页面中确实使用了数量很多的 SVG,我尝试着将 SVG 的 display: inline-block 改成 display: none 后,CPU 居然迅速降了下来,性能上也获得了缓解(以下图),感到很是惊喜。

「 注释 SVG 样式以前 」

「 注释 SVG 样式以后 」

但目前性能问题已然解决,而且 Webnovel 全站都使用的了 SVG ,此时若全局替换掉 SVG 未免有些粗暴,加之 Jake 的 demo 还不足以说明过多的 SVG 影响了页面的性能,所以该方案并无实施,但这确实能够做为 Webnovel 阅读页性能问题的另外一个突破点,后续还须要花更多的时间来探索这其中的奥秘。

一些碎碎念

固然,问题的修复老是须要总结一下:

  • visibility: hidden 是把双刃剑。
  • clientWidth、clientHeight 等操做在动画过程当中不建议使用。
  • 若是性能工具展现了大面积飙绿现象,请第一时间注释你的 CSS 再一探究竟
  • 没法定位问题的时候能够对比其余相似的网站获得启发,从而进一步定位
  • Edge 浏览器渲染层面和 Chrome 略有不一样,这是值得重视的。

性能优化本就是积极尝试和勇于试错的过程,正所谓积跬步以致千里,点滴的积累和探索不是为了产出 “ 最佳性能优化方案 n 条 ” ,而是为了能更好地提炼定位问题的思路、扩展认知范围、同时造成一种对待性能瓶颈不害怕的态度,但愿咱们均可以作到微笑面对性能瓶颈。


参考连接


查看更多分享,请关注阅文集团前端团队公众号:

相关文章
相关标签/搜索