1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > 前端性能优化(慕课网笔记)-2-渲染优化(浏览器)

前端性能优化(慕课网笔记)-2-渲染优化(浏览器)

时间:2022-09-05 12:04:44

相关推荐

前端性能优化(慕课网笔记)-2-渲染优化(浏览器)

一,浏览器渲染原理

1,页面渲染的流程

当浏览器拿到资源后,做了啥才将页面呈现给用户?

2,布局(回流)与绘制(重绘)

第一次布局叫布局,后续DOM操作等导致的布局叫回流。

影响回流的操作:

可以打开rendering查看情况:绿色的部分就是发生了重绘的部分:

<!DOCTYPE html><html><head><meta charset="utf-8"><title></title><style type="text/css">.test{width: 200px;height: 200px;background-color: red;}</style></head><body><div class="test"></div><div class="ulbox"><ul class="oul"><li>第一个</li><li>第二个</li><li>第三个</li><li>第四个</li></ul><button id="btn">增加一个li(回流加重绘)</button><button id="btn2">改变背景色(仅仅重绘)</button></div><script type="text/javascript">var oul=document.getElementsByClassName('oul')[0]var test=document.getElementsByClassName('test')[0]var btn=document.getElementById('btn')var btn2=document.getElementById('btn2')btn.onclick=function(e){var newP = document.createElement("li");var textNode = document.createTextNode("新的节点");newP.appendChild(textNode);oul.appendChild(newP)}btn2.onclick=function(e){if(test.style.backgroundColor=='red'){test.style.backgroundColor='green'}else{test.style.backgroundColor='red'}}</script></body></html>

然后再看持续性地强制回流:

const update=(timerstamp)=>{test.style.width=((Math.sin(test.offsetTop+timerstamp/1000)+1)*500)+'px'window.requestAnimationFrame(update)}window.addEventListener('load',update)

这个动画,一直读取toffsetop的值,就会导致回流一直在发生。

而避免回流和重绘可以采用一个插件:fastdom

/wilsonpage/fastdom

将读与写区分开来进行操作了。

const update=(timerstamp)=>{//通过fastdom.measure进行读操作fastdom.measure(()=>{var top=test.offsetTop//利用fastdom.mutate进行写操作fastdom.mutate(()=>{test.style.width=((Math.sin(top+timerstamp/1000)+1)*500)+'px'})})window.requestAnimationFrame(update)}window.addEventListener('load',update)

3,复合线程(compositor thread)与图层

为了提高绘制的效率,渲染器采取了复合的手段,相当于ps中的图层,每一图层绘制一部分内容,当所有图层绘制完毕,组合到一起,就是我们的页面绘制完成。

这样,有的时候,我们页面的元素发生变化,就可以只让他影响某一个图层发生变化,而不是全部图层发生变化。这样一来,我们的绘制工作就可以更加高效地完成。

拆分图层的依据:会相互影响的发放置到同一个图层。

那如何查看图层:ctrl+shift+p,搜索打开layers:

4,只触发 复合,但不会触发回流和重绘的几个属性

所以,动画的制作用这个比改变top,left等值要来得性能高。

但是们也不应该全部都创建成一个新的图层,因为图层太多了,开销也太大。

可以对比一下:

<!DOCTYPE html><html><head><meta charset="utf-8"><title></title></head><style type="text/css">.box{width: 1300px;height: 1000px;margin: 0 auto;}.oul{width: 100%;height: 100%;display: flex;justify-content: space-between;list-style: none;}.oli{flex-shrink: 0;margin: 10px;display: flex;justify-content: center;align-items: center;width: 200px;height: 200px;background-color: yellow;transition: all 1s linear;}.oli:hover .odiv{width: 150px;height: 150px;}.odiv{width: 100px;height: 100px;background-color: red;}</style><body><div class="box"><ul class="oul"><li class="oli"><div class="odiv"></div></li><li class="oli"><div class="odiv"></div></li><li class="oli"><div class="odiv"></div></li><li class="oli"><div class="odiv"></div></li><li class="oli"><div class="odiv"></div></li><li class="oli"><div class="odiv"></div></li></ul></div></body></html>

这种改变宽高的部分会引起回流和重绘:

然后改写代码为tranform,这种css3的动画,是只影响复合,并不会引发回流和重绘:

.oli:hover .odiv{transform: scale(1.5);transform:rotate(45deg);}

因为上个我不懂看。。。来自菜鸡的悲伤!!!!所以机智的我就又写了一个:

<!DOCTYPE html><html><head><meta charset="utf-8"><title></title></head><style type="text/css">.box{width: 1300px;height: 300pxpx;margin: 0 auto;}.oul{width: 100%;height: 200px;display: flex;justify-content: space-between;list-style: none;}.oli{flex-shrink: 0;margin: 10px;display: flex;justify-content: center;align-items: center;width: 200px;height: 200px;background-color: yellow;}.active{animation: move 3s infinite linear;}.odiv{width: 100px;height: 100px;background-color: red;}.btn{display: block;margin: 0 auto;}@keyframes move{0%{transform: rotate(0deg);}100%{transform: rotate(360deg);}}</style><body><div class="box"><ul class="oul"><li class="oli" ><div class="odiv" id="thirst"></div></li><li class="oli"><div class="odiv"></div></li><li class="oli"><div class="odiv"></div></li><li class="oli"><div class="odiv"></div></li><li class="oli"><div class="odiv"></div></li><li class="oli"><div class="odiv"></div></li></ul></div><button class="btn">按钮</button><script type="text/javascript">var firstodiv =document.getElementById('thirst')var btn=document.getElementsByClassName("btn")[0]btn.onclick=function(){firstodiv.classList.add('active')}</script></body></html>

点击按钮后,第一个方块会不断旋转:

先查看无操作的时候的performance:主线程没有任何回流重绘:

而触发tranform动画之后,我看了下,只有动画开始的时候会产生回流和重绘

作为对比,我把这个动画改成宽高改变的:

@keyframes move{0%{width: 100px;height: 100px;}50%{width: 150px;height: 150px;}100%{width: 100px;height: 100px;}}

现在来看就很明显了:

图中的每一个波峰都进行了回流和重绘的操作!主线程一直在间隙地被占用!!!

终于看懂了!流下了木有技术地泪水!!!!!呜呜呜!!

更加直观的观察方式是打开观察重绘的选项:至于怎么打开:可以按ctrl+shift+p然后搜索rendering就可以看到了:

第一种情况使用tranform,仅仅动画开始的一瞬间变成绿色(引发重绘):

第二种情况,必然是绿色的框一直存在,代表着一直在重绘:

所以,我们做动画的时候,尽量用transform

当然,要提取到新的图层,还需要:

will-change: 'transform';

二,高频事件防抖

如滚动,鼠标滑动等事件,都算高频事件。如果任务量太重,没有办法在16ms内完成一帧,就会产生抖动、卡顿的问题。

<!DOCTYPE html><html><head><meta charset="utf-8"><title></title></head><style type="text/css">.box{width: 1300px;height: 1000pxpx;margin: 0 auto;}.oul{width: 100%;height: 200px;display: flex;justify-content: space-between;list-style: none;}.oli{flex-shrink: 0;margin: 10px;width: 200px;height: 200px;list-style: none;background-color: yellow;}</style><body><div class="box"><ul class="oul"><li class="oli" ></li><li class="oli"></li><li class="oli"></li><li class="oli"></li><li class="oli"></li><li class="oli"></li></ul></div><script type="text/javascript">var lis=document.getElementsByClassName("oli")function changeWith(rand){for(let i=0;i<lis.length;i++){lis[i].style.width=(Math.sin(rand/1000)+1)*100+'px'}}window.addEventListener('mousemove',(e)=>{console.log("aa")let pos=e.clientXchangeWith(pos)})</script></body></html>

查看状态:一直在进行回流和重绘:

这就导致实际的场景中,动画会变得卡顿。有一个函数requestAnimationFrame可以帮助我们解决这个问题。

但是讲这个函数之前,需要先了解下每产生一帧动画所做的事情;

1,RAF时间调度

使用RAF的方法,把要处理的事情放在这里面就可以了:

window.addEventListener('mousemove',(e)=>{let pos=e.clientXwindow.requestAnimationFrame(()=>{changeWith(pos)})})

2,防抖:一段时间内没触发,才可以执行下一次

原理:当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又一次触发了事件,就重新开始延时。防抖有一个问题:那就是如果事件一直在触发,那么执行函数永远都得不到执行。这种情况下,函数节流此时是较好的方法。

<!DOCTYPE html><html><head><meta charset="utf-8"><title></title></head><style type="text/css">.box{width: 1300px;height: 1000pxpx;margin: 0 auto;}.oul{width: 100%;height: 200px;display: flex;justify-content: space-between;list-style: none;}.oli{flex-shrink: 0;margin: 10px;width: 200px;height: 200px;list-style: none;background-color: yellow;}</style><body><div class="box"><ul class="oul"><li class="oli" ></li><li class="oli"></li><li class="oli"></li><li class="oli"></li><li class="oli"></li><li class="oli"></li></ul></div><script type="text/javascript">var lis=document.getElementsByClassName("oli")function changeWith(rand){for(let i=0;i<lis.length;i++){lis[i].style.width=(Math.sin(rand/1000)+1)*100+'px'}}let ticking=falsewindow.addEventListener('mousemove',(e)=>{let pos=e.clientX//若事情还在执行,则是true,不执行事件if(ticking) return//改成true,事情开始执行ticking=truewindow.requestAnimationFrame(()=>{changeWith(pos)//执行完毕,又可以变成false(又可以执行事情了)ticking=false})})</script></body></html>

这样处理之后,也就是只有本次事情处理完毕之后才能开始处理下一个事情(其实我感觉这特么是节流啊!!!!视频里面说这是防抖???)。也就是鼠标的移动事件不再根据鼠标移动来触发。而是根据raf来调度触发。保证每一秒都可以生成60帧的动画。

三,react的时间调度实现

底层的原理就是这个RAF

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。