前端性能优化之渲染优化

页面渲染过程

为了使每一帧页面渲染的开销都能在期望的时间范围内完成。就需要开发者了解渲染过程的每个阶段,以及各阶段中有哪些优化空间是我们力所能及的。经过分析根据开发者对优化渲染过程的控制力度,可以大体将其划分为5各部分:js处理、计算样式、页面布局、绘制与合成。这个过程中的每一个阶段都有可能产生卡顿。注意:并非对于每一帧画面都会经历这5个部分,比如仅修改与绘制相关的属性(文字颜色,背景图片或边缘阴影等),而未对页面布局产生任何修改,那么在计算样式阶段完成后,便会跳过页面布局直接执行绘制。如果所更改的属性既不影响页面布局又不需要重新绘制,便可直接跳到合成阶段执行。具体修改哪些属性会触发页面布局、绘制或合成阶段的执行,这与浏览器的内核存在一定关系。

属性 Blink Gecko Webkit
z-index 绘制/合成 绘制/合成 布局/绘制/合成
transform 合成 合成 布局/绘制/合成
opacity 绘制/合成 合成 布局/绘制/合成
min-width 布局/绘制/合成 布局/合成 布局/绘制/合成
color 布局/绘制 布局/绘制 布局/绘制/合成
background 布局/绘制 布局/绘制 布局/绘制/合成
border-radius 布局/绘制 布局/绘制 布局/绘制/合成
border-style 布局/绘制/合成 布局/绘制/合成 布局/绘制/合成
border-width 布局/绘制/合成 布局/绘制/合成 布局/绘制/合成

Google的chrome实验室在网站上列出了许多css属性的详细表现,可以自行查看。

js执行优化

实现动画效果

推荐使用requestAnimationFrame方法来实现动画效果,requestAnimationFrame方法的执行时机会与系统的刷新频率同步。这样就能保证回调函数在屏幕的每次刷新间隔中只被执行一次,从而避免因随机丢帧而造成的卡顿现象。

其使用方法也十分简单,仅接受一个回调函数作为入参,即下次重绘之前更新动画帧所调用的函数。返回值为一个long类型整数,作为回调任务队列中的唯一标识,可将该值传给window.cancelAnimationFrame来取消回调,以某个目标元素的平移动画为例:

let start;
//定义目标动画元素
const element = document.getElementById("MyAnimate");
element.style.position = 'absolute'
//定义动画回调函数
function updateScreen(timestamp){
   
   
   if(!start) start = timestamp;
   //根据时间戳计算每次动画位移
   const progress = timestamp-start;
   element.style.left = "12px"
   if(progress<2000) window.requestAnimationFrame(updateScreen)
}
//启动动画回调函数
window.requestAnimationFrame(updateScreen)

除了通过让回调函数的触发时机与系统刷新频率同步来消除动画的丢帧卡顿,requestAnimationFrame方法还能通过节流不必要的函数执行,来帮助cpu的节能。
具体而言,对于cpu节能方面,考虑当浏览器页面最小化或被隐藏起来时,动画对用户来说是不可见的,那么刷新动画所带来的页面渲染就是对cpu资源的浪费,完全没有意义。
当创建setInterval定时器后,除非显式调用clearInterval去销毁该定时器,不然在后台的动画任务会不断执行,而requestAnimationFrame方法则完全不同,当页面未被激活时,屏幕刷新任务会被系统暂停,只有当页面被激活时,动画任务才会被激活并从上次暂停的地方继续执行,所以能有效地节省cpu开销。
在页面地一些高频事件中,比如页面滚动的scroll、页面尺寸更改的resize,需要防止在一个刷新时间间隔内发生多次函数执行,也就是所谓的函数节流。对60hz的显示器来说,差不多每16.7ms刷新一次,多次绘制并不会在屏幕上体现出来,所以requestAnimationFrame方法仅在每次刷新周期中执行一次函数调用,既能保证动画的流畅性又能很好地节省函数执行地冗余开销。

恰当使用web worker

js是单线程执行的,所有任务放在一个线程上执行,只有当前一个任务执行完才能处理后一个任务,不然后面的任务只能等待,这就限制了多核计算机充分发挥它的计算能力。同时在浏览器上,js的执行通常位于主线程,这恰好与样式计算、页面布局以及绘制一起,如果js运行时间过长,必然就会导致其他工作任务的阻塞而造成丢帧。
为此可将一些纯计算的工作迁移到web worker上处理,它为js的执行提供了多线程环境,主线程通过创建出worker子线程,可以分担一部分自己的任务执行压力。在worker子线程上执行的任务不会干扰主线程,待其上的任务执行完成后,会把结果返回给主线程,这样的好处是让主线程可以更专注地处理ui交互,保证页面的使用体验流程。需要注意的是,worker子线程一旦创建成功就会始终执行,不会被主线程上的事件所打断,这就意味着worker会比较耗费资源,所以不应当过度使用,一旦任务执行完毕就应及时关闭。除此之外,在使用中还有以下几点应当注意。

  • dom限制:worker无法读取主线程所处理网页的dom对象,也就无法使用document、window和parent等对象,只能访问navigator和location对象。
  • 文件读取限制:worker子线程无法访问本地文件系统,这就要求所加载的脚本来自网络。
  • 通信限制:主线程和worker子线程不在同一个上下文内,所以它们无法直接进行通信,只能通过消息来完成。
  • 脚本执行限制:虽然worker可以通过xmlhttprequest对象发起ajax请求,但不能使用alert和confirm方法在页面中弹出提示。
  • 同源限制:worker子线程执行的代码文件需要与主线程的代码文件同源。

web worker的使用方法非常简单,在主线程中通过new Worker()方法来创建一个worker子线程,构造函数的入参是子线程执行的脚本路径,由于代码文件必须来自网络,所以如果代码文件没能下载成功,worker就会失败。


                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值