页面渲染:性能分析

栏目: CSS · 发布时间: 6年前

内容简介:Chrome DevTools的performance面板可以记录和分析页面在运行时的所有活动。配合无痕模式,可以避免chrome插件的影响。上面部分是帧数信息:

Chrome DevTools的performance面板可以记录和分析页面在运行时的所有活动。

配合无痕模式,可以避免chrome插件的影响。

帧率图

1. 录制查看

页面渲染:性能分析

上面部分是帧数信息:

1312.3ms
1fps

下面部分是网页快照,浏览器按照一定时间间隔截取。

2. 实时查看

快捷键 ctrl + shift + p 可以实时查看帧率

页面渲染:性能分析

还可以进行其它配置

页面渲染:性能分析
  • Paint Flashing 高亮显示网页中需要被重绘的部分。
  • Layer Borders 显示Layer边界。
  • FPS Meter 每一秒的帧细节,帧速率的分布信息和GPU的内存使用情况。
  • Scrolling Performance Issues 分析鼠标滚动时的性能问题,会显示使屏幕滚动变慢的区域。
  • Emulate CSS Media 仿真CSS媒体类型,查看不同的设备上CSS样式效果,可能的媒体类型选项有print、screen

火焰图

页面渲染:性能分析
  • Loading:网络通信和HTML解析
  • Scripting:Javascript执行
  • Rendering:样式计算和布局,即重排
  • Painting:重绘 对应的详细事件

怎么看出性能问题?

1. 红色小三角

页面渲染:性能分析

常见的原因为:

  • handler took xxx ms 操作消耗太多时间
  • forced reflow is likely performance bottleneck “强制同步布局”可能会导致性能问题,通常是因为修改样式后读取属性,导致了浏览器必须重新渲染以获取最新的属性值。

2. 布局抖动

页面渲染:性能分析

“布局抖动”是指反复出现“强制同步布局”情况。 这种情况会在 JavaScript 从 DOM反复地写入和读取时出现,将会强制浏览器反复重新计算布局。布局抖动也会导致长帧,使页面卡顿。

3. 长帧

页面渲染:性能分析

长帧表示一帧的时间过长,会影响页面的加载速度与动画的流畅性,这时会感受到页面加载慢或动画卡顿。

动画播放时每秒的帧数最好能够达到60帧,也就是每帧16.6ms。

实例

1. 解析html(不包含js css外部文件)

页面渲染:性能分析
  • readystatechange(第一个)(文档已加载和解析) 此时状态为interactive,表示文档已加载和解析但资源仍在加载,该状态通常紧接着会触发DomContentLoaded。
  • DOMContentLoaded (DOM树构建完成) html文档被加载和解析成功,DOM树构建完成时会触发。
  • Recalculate Style(CSSOM构建完成) 通过添加和删除元素,更改属性、类或通过动画来更改 DOM,全都会导致浏览器重新计算元素样式,在很多情况下还会对页面或页面的一部分进行布局(即自动重排)。重新计算样式的步骤可以分为两步:
  1. 浏览器计算出给指定元素应用哪些类、伪选择器和 ID。
  2. 从匹配选择器中获取所有样式规则,并计算出此元素的最终样式。
  • readystatechange(第二个)(文档已加载和解析,且资源也加载完成) 此时状态为complete,表示文档和资源都已加载完成,该状态通常紧接着会触发load。
  • load事件 文档和资源都已加载完成时会触发。
  • Layout 布局几乎总是作用在整个文档,但还是主要看影响的节点个数。
页面渲染:性能分析

2. 解析html(包含js css外部文件)

页面渲染:性能分析
  • Evaluate Script (执行js)
  • layout变为不在ParseHtml中执行 可能是因为CSS文件或JS文件的加载阻塞了整个页面的渲染过程,因为js和css都可能对标签进行样式的设置。如果不存在文件,就不会存在等待加载的问题。

3. 改变背景色(重绘)

页面渲染:性能分析

4. 改变高度(重排)

页面渲染:性能分析

相对于重绘多了个Layout

5. 图片资源加载(img或bg)

如果图片标签尺寸不变,则会触发一次重绘

页面渲染:性能分析

6. 浏览器插入一段script

  • 未插入
页面渲染:性能分析
  • 插入
页面渲染:性能分析

可以发现只要插入script,不管有没有改变dom,chrome浏览器在之后马上会进行一次重排。

遵循的原则

1. 关于阻塞

  • css 不会阻塞 DOM树的解析
  • css加载 会阻塞 js ,从而阻塞了DOM树的解析,页面渲染(所以内联样式性能较高,适用于第一屏)
  • js 会阻塞 DOM树的解析 (因为js会改变DOM树内容)
  • css引入的字体文件加载 也会阻塞 js , 页面渲染

2. 关于与页面渲染过程的对应

  1. js执行时 :这时应该只是构建了前面部分的dom树和CSSOM树,因为js需要通过dom api和CSSOM api操作前面部分的标签的内容和样式。
  2. DOM树构建完成 :DomContentLoaded事件
  3. CSSOM构建完成、Render Tree构建完成 :Recalculate Style
  4. Layout :Layout事件
  5. paint :Paint(图片层绘制) 和 Composite Layers(图片层合并), 除了transform 或 opacity属性之外,更改任何属性始终都会触发绘制Paint
  6. reflow重排 :3 4 5步走一遍
  7. repaint重绘 :3 5步走一遍
  8. 更改一个既不要布局也不要绘制的属性:3步 + Composite Layers,此行为在678重新渲染步骤中开销最小,适合动画或滚动,具体比如transfrom opacity。

3. 关于chrom浏览器的一些行为

  • 渲染队列:浏览器存在一个队列,用于将多次的回流和重绘变成一次。当你读取DOM的属性和方法时,chrome不会清空队列(网上说会清空队列,立即进行回流和重绘),如果读取时发现样式有改变,chrome只是会Recalculate Style重新计算样式,如果没有改变,chrome甚至不会做出多余的操作。
  • html中的script标签执行完后一定会reflow

性能优化

阻塞优化

  • js存在问题: 1)js加载和执行都会阻塞DOM解析和页面渲染 2)如果引用第三方脚本,当第三方服务商请求延迟时,页面会白屏; js解决办法: 页面可以通过添加关键字defer和async来异步加载js。
页面渲染:性能分析
  1. 两者都是异步加载,不同的是: async(参考ajax):异步加载时不会阻塞DOM解析和页面渲染;执行时间为加载完成时;执行时会阻塞,但这时可能DOM已经解析完成,甚至页面已经渲染;另外会影响js文件执行顺序。 defer:异步加载时不会阻塞DOM解析和页面渲染;执行时间为DOM解析完成之后、DOMContentLoaded事件之前(所以会阻塞DCLoad事件和jquery的ready事件);执行时DOM已经解析完成,只会阻塞页面渲染。
  2. js文件加载顺序 同步 > 异步 同是async 按加载完成顺序 同是defer 按引入顺序 async defer 正常不一起用

减少重新渲染

关于CSS

  • 使用简单的样式表。样式表越简单,重排和重绘就越快。具体为:
  1. 减低选择器的复杂性,少用伪类;使用以类为中心的方法,例如BEM;
  2. 减少必须计算其样式的元素的数量,应当尽可能减少声明为无效的元素的数量。
  • 减少DOM元素层级。重排和重绘的DOM元素层级越高,成本就越高。
  • 多利用display:none。display:none的元素没有在渲染树,因而也不会进行重排和重绘
  • 使用CSS动画而不是JS动画。CSS动画优于JS动画,是由于CSS改变的是translate的值,不会引起offsetLeft、offsetTop等位置值的改变。
  • 使用absolute而不是float。position属性为absolute或fixed的元素在重排的开销比float少,因为不用考虑它对其他元素的影响。
  • 使用div而不是table。因为一个很小的改动可能都会引起整个table的重新布局,比如说td内容改变。

关于JS

  • DOM的多个读操作(或多个写操作)应该放在一起,不要穿插进行。因为 连续地设置元素样式(写操作),浏览器会一次性执行,即只触发一次重排或重绘 ,但如果在几个写操作间插入读取样式的操作,浏览器则不得不立即重排或重绘。(好像不生效)
  • 一次性改变样式。不要一条条地改变样式,而要通过改变class,或者el.style.csstext属性。
  • 使用离线DOM来改变元素样式。比如 cloneNode() 克隆节点,然后再替换掉元素节点 或者 display:none → 改动 → 显示
  • 尽量修改层级较低的DOM。
  • 不要在循环中重复读取DOM节点属性值。
  • 使用 window.requestAnimationFrame()、window.requestIdleCallback() 这两个方法控制重新渲染。

提高fps(frame per second)

网页动画的每一帧(frame)都是一次重新渲染,将一帧送到屏幕会采用如下顺序:

页面渲染:性能分析

帧数与动画流畅度的关系如下:

fps > 刷新频率

这里想说地是 fps最好能达到浏览器刷新频率 ,因而在播放动画的时候,注意不要执行太多耗时耗性能的操作。

手动控制重新渲染

window.requestAnimationFrame() 方法可以将某些代码统一放到下一次重新渲染时执行。具体是将js代码放在下一帧开始时执行。如果使用setTimeout 或 setInterval 来执行动画之类的视觉变化,其回调可能在帧的某个时间点执行,可能在末尾,这会使我们丢失帧,导致卡顿。

  1. 处理“布局抖动” 反复读写属性会导致布局抖动,导致长帧。
function doubleHeight(element) {
    var currentHeight = element.clientWidth;
    element.style.width = (currentHeight / 2) + 'px';
    element.style.height = '80px';
}
var elements = document.getElementsByTagName('tr');
for (var i = 0; i < elements.length; i++) {
    doubleHeight(elements[i]);
}
复制代码
页面渲染:性能分析

将doubleHeight函数改成下面这样:

function doubleHeight(element) {
    var currentHeight = element.clientHeight;
    window.requestAnimationFrame(function () {
      element.style.height = (currentHeight * 2) + 'px';
    });
}
复制代码
页面渲染:性能分析

2)页面滚动事件(scroll)

$(window).on('scroll', function() {
   window.requestAnimationFrame(scrollHandler);
});
复制代码

3)最适合用于动画

结合项目

1)现在项目中,页面(以“任务”页面为例)在加载时都会请求一些ajax数据,比如datagrid,tree数据等等,还有些ajax数据只是预加载。如果这些ajax在页面渲染前完成请求,则会阻塞页面渲染。所以同一个页面不同网速下会有两种渲染顺序:

  • 在渲染之后执行
页面渲染:性能分析
  • 在渲染之前执行
页面渲染:性能分析

解决办法

  1. 在所有资源加载完后进行ajax请求。将datagrid等控件的数据加载放在$(window).load()事件中。
  2. 延迟初始化modal中的内容

效果

页面渲染:性能分析
页面渲染:性能分析

问题

问:只要执行js,都会重排吗?

答:执行js的发生情况如下:

  • 载入页面时script标签:不管有无改变dom,都会重排
  • 异步ajax回调:需要判断有无改变dom
  • setTimeout:需要判断有无改变dom

问:请求js文件时,请求和执行顺序是什么?

答:请求会一起发出;执行顺序按引入的顺序,不会因为后一个先返回数据而先执行。

问:页面加载时提前ajax请求一些数据,会不会影响性能?

答:可能会,可能不会。ajax请求时不影响性能,请求后执行回调函数会影响。ajax回调函数会在请求完成且js主程序运行完后执行,等到我们能看到页面,需要经历页面解析和渲染这两个过程,如果页面渲染前ajax回调执行了,那将阻塞渲染过程。

问:为什么jquery通常在onReady中执行代码?

答:onReady 监测的是 DOMContentLoaded事件,也就是监测DOM树构建完成。

附录

loading 事件

事件 描述
Parse HTML 浏览器执行HTML文件解析
Parse Stylesheet 浏览器执行CSS文件解析(单指外部CSS文件)
Finish Loading 网络请求完毕事件
Receive Data 请求的响应数据到达事件,如果响应数据很大(拆包),可能会多次触发该事件
Receive Response 响应头报文到达时触发
Send Request 发送网络请求时触发

Scripting事件

事件 描述
Animation Frame Fired 一个定义好的动画帧发生并开始回调处理时触发
Cancel Animation Frame 取消一个动画帧时触发
GC Event 垃圾回收时触发
DOMContentLoaded 当页面中的DOM内容加载并解析完毕时触发
Evaluate Script A script was evaluated.
Event js事件
Function Call 只有当浏览器进入到js引擎中时触发
Install Timer 创建计时器(调用setTimeout()和setInterval())时触发
Request Animation Frame A requestAnimationFrame() call scheduled a new frame
Remove Timer 当清除一个计时器时触发
Time 调用console.time()触发
Time End 调用console.timeEnd()触发
Timer Fired 定时器激活回调后触发
XHR Ready State Change 当一个异步请求为就绪状态后触发
XHR Load 当一个异步请求完成加载后触发

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

孵化Twitter

孵化Twitter

[美]尼克·比尔顿(Nick Bilton) / 欧常智、张宇、单旖 / 浙江人民出版社 / 2014-1 / 49.90元

一个在挣扎中生存的博客平台Odeo,一小撮龙蛇混杂的无政府主义者员工,经历了怎样的涅槃,摇身一变,成为纽交所最闪耀的上市企业Twitter? 一个野心勃勃的农场小男孩,一个满身纹身的“无名氏“,一个爱开玩笑的外交家,一位害羞而又充满活力的极客,这四位各有特色的创始人如何从兢兢业业、每日劳作的工程师,成为了登上杂志封面、奥普拉秀和每日秀的富裕名人?而在Twitter日益茁壮成长的过程中,他们又......一起来看看 《孵化Twitter》 这本书的介绍吧!

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具