实例解析:《奇趣百科》性能优化

移动开发 移动应用 Android
奇趣百科年后进行了一次大改版, 不论是内容还是程序架构上,改版使用Vue.js的MVVM的理念令开发过程加速了不少。但是改版后却出现了明显的性能问题,出现了比较明显的页面卡顿,因此我们又专门做了一次性能优化。本文主要介绍Chrome DevTools中的Timeline Profils等工具的使用方法。

奇趣百科年后进行了一次大改版, 不论是内容还是程序架构上,改版使用Vue.js的MVVM的理念令开发过程加速了不少。但是改版后却出现了明显的性能问题,出现了比较明显的页面卡顿,因此我们又专门做了一次性能优化。本文主要介绍Chrome DevTools中的Timeline Profils等工具的使用方法。

1. 组件粒度加粗

首先最先想到的就是用Timeline看一下:

timline1.jpg

Frames情况还算正常, 但是注意到内存占用已经快17MB了, 相对于改版前的12MB是明显偏高的(由于篇幅关系就不上图了), 那么我们继续来追查内存相关的,使用Chrome开发者工具的Profiles看一下当前的内存占用情况:
打开Chrome开发者工具 -> 点击 Profiles 控制板 -> 选中 Take Heap Snapshot -> 点击 Take Snapshot

heap-snapshot1.jpg

查看了一下, 发现listItemHead, listItemImg , listItemMeta 和 listTuwen 组件对象分别都各有9个或者10个(9个是因为业务逻辑问题)。

Vue.js支持组件系统,因此,为了提高复用性,我把一个卡片定义为一个组件,而这个组件又由若干个组件组成。

4components.jpg

卡片本身是listTuwen组件, 该组件包括了三个组件: listItemHead, listItemImg 和 listItemMeta。

接下来我们来研究一下snapshot表格中相应的列分别代表什么。

一个对象有两种形式来持有内存:

  • 直接拥有

  • 间接引用

分别对应 snapshot 中的Shallow Size和Retained Size

Shallow Size

Shallow Size代表了对象直接持有的内存大小。一个标准的JS对象通常会持有用于描述自身逻辑和存储直接值(属性值)的内存。 通常情况下应该只有字符串和数组类型可能拥有一个较大的Shallow Size。

Retained Size

Retained Size代表了当前对象所引用的其他对象占用的内存大小. 当当前对象被销毁时, 这一部分的内存会被释放。

奇趣百科的首页触底加载一共可以加载30次一共300张卡片,我们加载30次完毕后与刚进入首页的情况进行对比:

heap-snapshot2.jpg

内存占用已经飙升到了70MB了, 我们切换到 Comparison 视图(红框), 并选择 Snapshot 1(红框)。

#New 一列说明了三个组件的对象都增加了290个(289的是因为业务逻辑), Size Delta 一列说明了三个组件的对象各自增加了快7M的内存, 加起来就是20+MB了。

因此我们可以得出这样一个结论: 同一页面中大量被重用的组件尽量不要嵌套其他组件, 不然内存占用会随着组件的增多而快速上升。

insideVue.jpg

可以看到一个Vue组件对象内部引用了大量的其他对象, 包括directives, watchers 等, 还有一些系列的 getter 和 setter 方法。

解决办法就是卡片内部不使用组件, 一个卡片就只有自身这个组件, 采用其他方法来提高代码的复用性。

1components.jpg

最后我们来对比优化后的结果:

heap-snapshot3.jpg

heap-snapshot3.jpg

触底加载完毕,300张卡片占用内存40M左右,虽然listTuwen组件的对象占用的内用大了很多,但是总体下降了40%,优化效果很明显。

移除视窗外的不必要的DOM

页面上DOM的数目越少,占用内存就越少,性能也就越好,这是很容易得出来的结论。

参照手机淘宝搜索结果页的做法, 我们可以把视窗外的不可见的卡片移除掉, 当这些卡片滚动回到窗口内(或者滚动位置接近到窗口的某个像素值)后再插入显示.列表的卡片数目保持在一个恒定的值,而不是直线增长下去.

dom.jpg

奇趣百科线上的代码可以看到,我们的卡片数目保持在30个,也就是 DOM 的数据是恒定的,不会随着页面越滚动到下面越多。

接下来我们看一下内存情况验证我们这样做的效果:

优化前后的 Timeline 工具的内存曲线:

dom2.jpg

内存曲线都成锯齿形状,有触发垃圾回收,但是优化后的曲线上升的斜率比优化前少了2°,即内存上升速度慢了。

另外是优化前后的 Profiles 对比('DOM'作为关键字进行筛选):

dom3.jpg

内存优化后少了近10M,并且DOM的数据减少了10倍,内存使用下降25%。

图片懒加载

进行这一个优化点之前,我们先来科普一下Timeline这个控制板。

最好在浏览器隐身模式下使用,禁用一切无关插件,因为插件也会占用内存,影响测试结果。如果需要记录网络请求的话, 最好把浏览器缓存也禁用掉。

网上盗的一张图(出处):

timeline-panel1.jpg

有三种模式可以切换关注点:

  • Events: 显示所有事件的记录

  • Frames: 显示页面渲染的帧数

  • Memory: 显示页面的内存情况

这里我们重点关注 Frames 模式。

页 面的每一帧内容都是GPU绘制出来的,它的最高绘制频率受限于显示器的刷新频率,大多数情况下最高的绘制平率只能是每秒60帧(frames per second, 即fps),对应于显示器的60Hz。因此在页面性能的测试中,60fps是一个非常重要的指标,越接近越好。

这里说到了一个常量 -- 屏幕刷新频率60Hz

60Hz 和60fps有什么关系?没有任何关系。fps代表GPU渲染画面的频率,Hz代表显示器刷新屏幕的频率。一幅静态图片,你可以说这副图片的fps是0帧 /秒,但绝对不能说此时屏幕的刷新率是0Hz,也就是说刷新率不随图像内容的变化而变化。游戏也好浏览器也好,我们谈到掉帧,是指GPU渲染画面频率降 低。比如跌落到30fps甚至20fps,但因为视觉暂留原理,我们看到的画面仍然是运动和连贯的。

Frames 模式模式中的 Frames 就是"帧". "一帧"(Frames模式下的一条柱子)代表了显示器为了在一帧()内展现内容所要完成的工作,包括执行JavaScript,处理事件,更新DOM,改变样式和布局还有绘制页面.

timeline-panel3.jpg

在Frame视图中有两条贯穿该视图的横线,分别标识出60FPS和30FPS的基准。

timeline-panel4.jpg

注意到有些柱子有一部分是空白的或者是灰色的,分别代表:

  • 空白: 空闲时间

  • 灰色: 没有被记录的活动,可以理解成是浏览器内部c++的一些工作,这部分和前端的js以及渲染没什么关系.

现在来看一下项目的 Frames 情况:

timeline-panel5.jpg

看到超出 60fps 的柱子还是挺多的, 而且都是柱子的大部分颜色都是绿色的。

先来说明一下柱子颜色的含义:

timeline-panel2.jpg

  • 蓝色: 网络和HTML解析

  • 黄色: JavaScript 脚本运行

  • 紫色: 样式重计算和布局 ( Layout , Recaculate Style, Update Layer tree)

  • 绿色: 绘制和合成 ( Paint , Composite Layers)

所以我们大部分时间话费在绘制上了. 我们再选取一些比较高的柱子, 看看都有什么特点:

timeline-panel6.jpg

我们看到有一些空的绿色色块和实心的绿色色块,是这样的:

绘制分两步走: 画和渲染

  • 画: 这包括了一些系列你想要画出来的东西, 而这些是由元素上的CSS而得来的。

  • 渲染: 逐条分析上一步中你想要"画"的东西, 利用 GPU 来组合填充这些东西的实际像素。

这一部分我翻译得很烂,因为我自己也不太懂具体的意思,所以大家可以看看原文 → About the green bars

而 Painting 包括了这些事件:

事件 描述 Composite Layers Chrome的渲染引擎完成图片层合并时触发 Image Decode 一个图片资源完成解码后触发 Image Resize 一个图片被修改尺寸后触发 Paint 合并后的层被绘制到对应显示区域后触发。

根 据我的观察, 我发现比较高的绿色柱子一般都包括多个Image Decode事件, 图片加载回来而触发了这个事件, 进而产生了大量的Rasterize Paint事件, 所以我猜测, 把图片都分开加载, 不要一次性就加载10张图片, 这样就得把事件分散, 高的柱子拆成多个矮的柱子, 这样就能进一步提升流畅度。

图片懒加载是怎么实现的就不细说了, 类似的效果可以参照淘宝首页。

最后我们来看一下优化后的 Timeline :

timeline-panel7.jpg

这个情况已经是达到比较理想的状态了, 实际操作也比较流畅。

但是值得一提的是, 最后 PM 并没有采纳这一步的优化方式,因为体验过后,PM认为图片懒加载反而会让用户觉得卡顿, 并不是实际滑动上的卡顿, 而是整体体验上的卡。所以最后从用户体验的角度出发, 我们的优化方案并没有采用图片懒加载。

责任编辑:chenqingxiang 来源: CocoaChina
相关推荐

2017-10-26 14:29:50

互动百科

2015-04-17 15:23:10

互动百科

2021-09-03 09:26:15

Python爬虫百度百科

2017-06-20 10:51:15

芒果

2017-02-09 17:05:03

2018-06-29 17:05:51

互动百科

2017-01-19 17:41:30

百科

2024-01-04 10:19:48

2022-12-30 13:05:05

云计算

2022-12-01 13:10:13

SASE网络

2013-10-30 16:54:46

维基百科维基百科的衰落

2023-02-13 11:43:26

CHATGPT人工智能

2015-06-23 11:23:26

行业百科频道

2017-06-27 14:36:03

移动 互联网

2016-12-06 14:43:00

互动百科

2011-11-25 12:44:28

2015-11-09 10:15:53

中国网科技频道

2022-11-22 15:33:29

工业物联网IIoT

2022-10-17 12:51:28

点赞
收藏

51CTO技术栈公众号