页面渲染优化

在这里插入图片描述1-3都非常快,4和5耗时。合称为渲染( render )
页面显示的时候,至少会渲染一次。用户交互访问的过程中,还会不断重复渲染。例如:
1、修改dom
2、修改样式
3、用户事件(mouseover/out,scroll,input,resize等)
这些都会导致页面的重新渲染
2.2重绘和重排的定义和区别
重新渲染,页面就需要重新生成布局和重新绘制。前者叫做重排,后者叫做重绘
重绘不一定需要重排,重排必然导致重绘
2.3重排和重绘对性能的影响
重排和重绘非常消耗资源,是导致页面性能降低的重要原因之一。但是页面却无法避免重排和重绘。因此想要提高网页性能,就要降低重排和重绘的频率和成本,尽可能的减少触发重排和重绘,并尽可能的控制在对小范围内元素进行重绘和重排
2.4浏览器实现重排和重绘

div.style.color = ‘red'
div.style.margin = ‘30px’

现在的浏览器比较智能。会尽量把所有的变动集中在一起,排成一个队列,然后一次性执行,尽量避免多次重新渲染。上面的代码中,div元素有两次样式变动,但是浏览器智慧触发一次重排和重绘

div.style.color = ‘red’
let margin = parseInt(div.style.marginTop)
div.style.marginTop = (margin + 10) + ‘px’

如果写的代码像上面的代码,就会触发两次重绘和重排。
上面代码对div元素设置背景色以后,第二行要求浏览器给出该元素的位置,所以浏览器不得不重排。
一般来说,样式的写操作之后,如果有下面这些属性的读操作,都会引发浏览器立即重新渲染

offsetTop/offsetLeft/offsetWidth/offsetHeight
scrollTop/scrollLeft/scrollWidth/scrollHeight
clientTop/clientLeft/clientWidth/clientHeight
getComputedStyle()

因此,从性能的角度考虑,尽量不要把读属性操作和写属性操作,放在一个语句里面

2.5优化技巧
1、DOM的多个读操作(写操作),应该放在一起。不要两个读操作之间加入一个写操作。
2、如果某个样式是通过重排得到的,那么最好缓存结果。避免下一次用到的时候,浏览器又要重排
3、不要一条条地改变样式,而要通过改变class,或者csstext属性,一次性的改变样式。
4、尽量使用离线dom,而不是真实的网页dom,来改变元素的样式。比如createDocumentFragment(),完成后再把这个对象加入dom。再比如,使用cloneNode()方法,在克隆的节点上进行操作,然后再用克隆的节点替换原始节点。
5、如果不可避免的需要对某元素进行多次操作,先隐藏该元素,然后对这个节点进行多次操作,完成后再恢复显示。用两次重新渲染,取代了可能高达n次的重新渲染。
6、position属性为absolute的元素,重排的开销会比较小,因为不用考虑它对其他元素的影响。
7、在必要的时候,才将元素的display属性为可见,因为不可见的元素不影响重排和重绘。另外,visibility:hidden的元素只对重绘有影响,不影响重排。
8、元素的层级越高重绘重排的成本就越高,因此尽可能的控制重排重绘的区域范围,尽可能在小范围内重绘重排。

优化技巧——window.requestAnimationFrame()使用
页面动画效果同时对多个元素进行修改,以及页面的滚动操作,不可避免的要对页面进行很频繁的重新渲染

Function increaseHeight(ele){
Let currentHeight = ele.clientHeight
Ele.style.height = (currentHeight + 20) + ‘px’
}
eleArrs.forEach(increaseHeight)

像上面代码将某个元素组里面的所有元素的高度都增加20px。可是,每次循环都是读属性操作后面跟着一个写属性操作。这样的代码会在短时间内触发大量的重新渲染,显然对于网页性能很不利。像这样的时候,可以使用window.requestAnimationFrame(),让读属性操作和写属性操作分离,把所有的写属性操作放到下一次重新渲染。

Function increaseHeight(ele){
Let currentHeight = ele.clientHeight
requestAnimationFrame(function(){
Ele.style.height = (currentHeight + 20) + ‘px’
})}
eleArrs.forEach(increaseHeight)

页面滚动事件(scroll)的监听函数,也很适合用window.requestAnimationFrame(),推迟到下一次重新渲染
例如:

$(ele).on(‘scroll’, function(){
requestAnimationFrame(eleScrollFn)
})

3.1脚本不能阻塞页面的加载
脚本放到页面底部,脚本按需动态加载

3.2合理利用ajax get方法请求数据
由于post请求无法在本地缓存数据,不管地址与能数有无变化,每次都需要从服务端获取。因此一般来说,处理向服务器提交数据之外,获取数据使用get方式.

3.3当某个函数因为客观原因不可避免需要长时间执行时,用setTimeout打断代码执行。以免页面卡死
例如:需要在页面同时通过不同数据创建10个以上的散点图实例,每个实例包含至少100个以上散点,单独每个实例渲染时间需要1000-2000毫秒,所有实例的渲染时间要远大于(1000-2000)*10,页面甚至会超出脚本最大执行时间,以致卡死奔溃。像这种情况,通过setTimeout创建多个队列创建实例,比如:

dataList.forEEach((data, index) => {
setTimeout(()=>{
new scatterplot(data, ‘ele_’,+index)
},25)
})

3.4避免多次从远端作用域取值,需要多次用到远端作用域时可使用局部变量进行缓存

Let currentId = this.options.currentData.id
List.forEach((item, index) => {
//if(item.id === this.options.currentData.id)
If(item.id === currentId){...}
})

3.5避免使用with,以防止增长作用域链
3.6条件判断时,把出现概率较高的条件放到上面,以减少条件判断的次数
3.7循环遍历取值时,找到值时记得跳出循环,避免不必要的循环
3.8尽可能的使用事件委托处理事件,表面项目因事件列表过多,导致内存增加,造成严重的性能问题

$(‘tableClass’).find(‘td’).bind(‘click’, handleClick)
$(‘tableClass’).bind(‘click’, handleClick)

3.9组件在关闭时都应该调用自己的destroy方法,清除自身创建的对象以及dom节点。避免内存不断增加
3.10如需对某个数组对象进行反复排序时使用,只在第一次使用sort,之后使用reverse方法

If(this.hasSorted){
Arr.reverse()
} else {
Arr.sort((a,b) => (return a-b))
}

3.11如果某个组件需要展示大数据时,应根据显示区域范围计算出需要显示的数据范围。避免数据过多时导致内存超标,dom节点超标,页面卡慢。
3.12短时间内需要多次请求更新数据时,应取消上次未返回数据的请求,再发起新请求

If(this.currentGetXXXData){
this.currentGetXXXData.abort()
}
this.currentGetXXXData = $.ajax(type:’get’,url:’xxx/xxx’)
//axios
Let cancelToken = axios.CancelToken
If(this.cancelGetXXXData){
this.cancelGetXXXData()
}
Axios.get(‘xxx/xxx’,{
CancelToken: new cancelToken(cancel => {
this.cancelGetXXXData = cancel
}),
Params: {...}
})

3.13短时间内需要多次执行某些方法时(比如:mouseover,scroll,resize,auto complete的keyup事件),使用防抖和节流方法控制减少执行次数

//防抖函数
function debounce( fn, delay ){ // 传一个回调函数
    let timer = null ;
return function( ){ 
var self = this
var args = arguments
        clearTimeout(timer) ; // 清除定时器
        timer = setTimeout( () => {
             fn.apply(self, args)
         }, delay)
    }
}
//节流函数
function throttle( fn ){
	let timer = null
	var startTime = Data.now()
return function(){
	var curTime = Date.now()
var remaining = delay – (curTime - startTime )
var self = this
var args = arguments
	clearTimeout(timer) ; // 清除定时器
        if(remainint < 0){ fn.apply(self, args); startTime = Data.now() } 
		else {
		timer = setTimeout( () => {
             fn.apply(self, args)
         }, remaining)
}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值