前端性能优化(慕课网笔记)-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

https://github.com/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.clientX
				changeWith(pos)
			})
		</script>
	</body>
</html>

在这里插入图片描述
查看状态:一直在进行回流和重绘:
在这里插入图片描述
这就导致实际的场景中,动画会变得卡顿。有一个函数requestAnimationFrame可以帮助我们解决这个问题。
但是讲这个函数之前,需要先了解下每产生一帧动画所做的事情;
在这里插入图片描述

1,RAF时间调度

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

window.addEventListener('mousemove',(e)=>{
				let pos=e.clientX
				window.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=false
			window.addEventListener('mousemove',(e)=>{
				let pos=e.clientX
				//若事情还在执行,则是true,不执行事件
				if(ticking) return
				//改成true,事情开始执行
				ticking=true
				window.requestAnimationFrame(()=>{
					changeWith(pos)
					//执行完毕,又可以变成false(又可以执行事情了)
					ticking=false
				})
			})
		</script>
	</body>
</html>

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

三,react的时间调度实现

底层的原理就是这个RAF
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值