60fps 与设备刷新率
1 .其中每个帧的预算时间仅比 16 毫秒多一点 (1 秒/ 60 = 16.66 毫秒)。但实际上,浏览器有整理工作要做,因此您的所有工作需要在 10 毫秒内完成。如果无法符合此预算,帧率将下降,并且内容会在屏幕上抖动。 此现象通常称为卡顿,会对用户体验产生负面影响
渲染基本流程
1 .JS :一般都是js操作,实现一些视觉变化效果
2 .样式计算:根据匹配选择器,计算出哪些元素应用哪些css规则的过程,知道规则,将应用规则并计算每个元素的最终样式
3 .布局:知道对一个元素应用哪些规则之后,浏览器即可以开始计算他要占用的空间大小和在屏幕中位置。网页的布局意味着一个元素可能会影响到其他元素,比如调整body的宽度就会影响到子元素的宽度以及树中各处节点。
4 .绘制:绘制时填充像素的过程,设计文本,颜色,图像,边框和阴影,绘制一般实在多个层上面完成的
5 .合成:由于页面的各个部分可能被分成多个层级,由此他们需要被正确按照顺序绘制到屏幕上,特别是对于重叠元素来说
6 .但是并不是每次都走这个全部的过程
7 . JS / CSS > 样式 > 布局 > 绘制 > 合成
1 .如果您修改元素的“layout”属性,也就是改变了元素的几何属性(例如宽度、高度、左侧或顶部位置等),那么浏览器将必须检查所有其他元素,然后“自动重排”页面。任何受影响的部分都需要重新绘制,而且最终绘制的元素需进行合成
8 .JS / CSS > 样式 > 绘制 > 合成
1 .如果修改paint only 属性,背景图片,文字颜色或者阴影,不会影响页面布局的属性,浏览器就会跳过布局,直接执行绘制
9 .JS / CSS > 样式 > 合成
1 .如果修改一个既不需要布局,也不需要绘制的属性,浏览器只会执行合成
2 .这个适合应用生命周期中的高压力点,比如动画或者滚动
3 .https://csstriggers.com/
4 .属性触发影响的部分
1 .优化js执行
1 .对于动画效果的实现,避免使用setTimeout,setInterval,要使用requestAnimationFrame
2 .将长时间运行的js从主线程移到web worker
3 .使用微服务来执行对多个帧的DOM更改
4 .使用chrome的性能工具来评估代码
requestAnimationFrame来实现视觉变化
1 .保证js在帧开始的时候运行的唯一办法就是使用requestAnimationFrame。相当于一个钩子函数
2 .框架或示例可能使用 setTimeout 或 setInterval 来执行动画之类的视觉变化,但这种做法的问题是,回调将在帧中的某个时点运行,可能刚好在末尾,而这可能经常会使我们丢失帧,导致卡顿
降低复杂性或者使用web worker
1 .JavaScript 在浏览器的主线程上运行,恰好与样式计算、布局以及许多情况下的绘制一起运行。如果 JavaScript 运行时间过长,就会阻塞这些其他工作,可能导致帧丢失
2 .在许多情况下,可以将纯计算工作移到web worker,这些计算不需要访问DOM,仅仅是数据便利,搜索或者排序,加载和生成模型也可以
3 .
2 缩小样式计算的范围并降低复杂性
1 .用于计算某元素计算样式的时间中大约有 50% 用来匹配选择器,而另一半时间用于从匹配的规则中构建 RenderStyle(计算样式的表示)
2 .降低选择器的复杂性,最好只有一个 .box:nth-last-child(-n+1) .title {
/* styles */
} 避免这样的
3 .BEM方式组织样式
3 避免大型,复杂的布局和布局抖动
1 .布局是浏览器计算各元素几何信息的过程。元素的大小以及在页面中的位置,根据所用的css,元素的内容或者父级元素每个元素都将有显式或者隐含的大小信息
2 .主要有一下元素影响性能
3 .需要布局的元素的数量,布局的复杂度
尽可能避免布局操作
1 .布局几乎总是作用到整个文档,如果有大量元素,将需要很长时间来计算所有元素的位置和尺寸
2 .新的flex布局会显著减少布局的时间
3 .
避免强制同步布局
1 .先读取样式值,然后进行样式更改
2 .如果先进行样式更改,在读取的时候,会打乱本来的渲染顺序
3 .js在运行 的时候,来自上一帧的旧布局的值是知道的。
function logBoxHeight() {
box.classList.add('super-big');
//先改变了样式
console.log(box.offsetHeight);
//然后再求他的值,就会打乱顺序,浏览器必须先使用新的样式,然后再进行布局,最后返回新的高度。但是这是不必要的
}
4 .因此,始终应该批量读取样式,然后执行任何写的操作
function logBoxHeight() {
// Gets the height of the box in pixels
// and logs it out.
console.log(box.offsetHeight);
box.classList.add('super-big');
}
5 .大多数情况,不需要应用样式然后查询,使用上一帧的值就足够了
function resizeAllParagraphsToMatchBlockWidth() {
// Puts the browser into a read-write-read-write cycle.
for (var i = 0; i < paragraphs.length; i++) {
paragraphs[i].style.width = box.offsetWidth + 'px';
//这里其实更加糟糕,每次都会进行强制布局。
需要把这个缓存起来
}
}
//正确写法
var width = box.offsetWidth;
function resizeAllParagraphsToMatchBlockWidth() {
for (var i = 0; i < paragraphs.length; i++) {
// Now write.
paragraphs[i].style.width = width + 'px';
}
}
5 简化绘制的复杂度,减小绘制区域
1 .绘制是填充像素的过程,像素最终合成到用户的屏幕上。是执行时间最长的任务。
2 .除transform,opacity属性之外,更改任何属性都会触发绘制
3 .可以通过提升层和动画的编排来减少绘制区域
4 .Show paint rectangles
5 .每次调试,感觉不应该发生绘制的地方发生了绘制,那就是问题,每次绘制部分都会闪烁绿色
6 .常见新层的最佳方式就是will-change属性
.moving-element {
will-change: transform;
}
//不支持的情况
.moving-element {
transform: translateZ(0);
}
7 .层也有开销,不要再不分析的情况下提升元素
8 .尽可能的避免绘制的发生,特别是在动画效果中。因为每帧 10 毫秒的时间预算一般来说是不足以完成绘制工作的,尤其是在移动设备上。
6 坚持仅合成器的属性和管理层计数
1 .react router最新的react router
使输入处理程序去除抖动
1 .输入处理程序可能是应用最容易出现性能的问题,因为他们可能阻止帧完成,并导致额外且不必要的布局工作
2 .避免长时间运行输入处理程序,他们可能阻止滚动
3 .不要再输入处理程序种进行样式更改
4 .使用处理层序去除抖动,存储事件值在下一个requestAnimationFrame回调中处理样式更改
5 .如果您附加一个输入处理程序,例如 touchstart、touchmove 或 touchend,则合成器线程必须等待此处理程序执行完成,因为您可能选择调用 preventDefault() 并且会阻止触摸滚动发生。即使没有调用 preventDefault(),合成器也必须等待,这样用户滚动会被阻止,这就可能导致卡顿和漏掉帧。确保您运行的任何输入处理程序应快速执行,并且允许合成器执行其工作
6 .感觉这里遇到最复杂的就是虚拟列表了,每次滚动都需要做计算
7 .使滚动处理程序去除抖动
function onScroll (evt) {
// Store the scroll value for laterz.
lastScrollY = window.scrollY;
// Prevent multiple rAF callbacks.
if (scheduledAnimationFrame)
return;
scheduledAnimationFrame = true;
requestAnimationFrame(readAndUpdatePage);
}
window.addEventListener('scroll', onScroll);
测试网页性能
1 .https://developers.google.com/web/fundamentals/performance/audit
网友评论