性能优化都能做什么?来我们一起学习呀

230 阅读2分钟

这是我参与 8 月更文挑战的第 12 天,活动详情查看: 8月更文挑战

性能优化原则

  • 多使用内存、缓存或其它方法
  • 减少CPU计算量,减少网络加载耗时
  • 适用于所有变成的性能优化—— 空间换时间

性能优化如何入手

  • 让加载更快
    • 减少资源体积:压缩代码
    • 减少访问次数: 合并代码,SSR服务器端渲染,缓存
    • 使用更快的网络:CDN
  • 让渲染更快
    • css放在head,js放在body最下面
    • 尽早开始执行JS, 用DOMContentLoaded触发
    • 懒加载(图片懒加载,上滑加载更多)
    • 对DOM查询进行缓存
    • 频繁DOM操作,合并到一起插入DOM结构
    • 节流throttle,防抖debounce

缓存

  • 静态资源加hash后缀,根据文件内容计算hash
  • 文件内容不变,则hash不变,则url不变
  • url和文件不变,则会自动厨房http缓存机制,返回304

SSR(serve site render)

  • 服务器端渲染:将网页和数据一起加载,一起渲染
  • 非SSR(前后端分离): 先加载网页,再加载数据,再渲染数据
  • 早先的JSP ASP PHP(都是ssr),现在的vue React SSR

懒加载

<img id='img1' src='priview.png' data-realsrc='real.png' />
<script type='text/javascript'>
	// 默认加载预览图,上滑时把图片真正的地址赋值给src
	var img1 = document.getElementById('img1')
	img1.src = img1.getAttribute('data-realsrc')
</script>

缓存DOM查询

// 不缓存DOM查询结果
for(let i=0; i<document.getElementsByTagName('p').length;i++){
	//每次循环,都会计算length,频繁进行DOM 查询
}

// 缓存DOM查询结果
const pList = document.getElementsByTagName('p')
const length = pList.length
for(let i=0; i<length;i++){
	//缓存length,只进行一次DOM 查询
}

多个DOM操作 一起插入到 DOM结构

const listNode = document.getElementById('list')

// 创建一个文档片段,此时还没有插入到DOM树中
const frag = document.createDocumentFragment()

//执行插入
for(let i=0;i<10;i++){
	const li = docuemnt.createElement('li')
	li.innerHTML = 'list item ' + i
	frag.appendChild(li)
}

// 都完成之后,再插入到DOM树中
listNode.appendChild(frag)

尽早开始JS执行

window.addEventListener('load',function(){
	//页面的全部资源加载完才会执行,包含图片、视频等
})
document.addEventListener('DOMContentLoaded',function(){
	//DOM渲染完成即可执行,此时图片、视频还可能没有加载完
})

实例

手写防抖 debounce

  • 监听一个输入框的,文字变化后触发 change事件
  • 直接用keyup事件,则会频繁触发change事件
  • 防抖: 用户输入结束或暂停时,才会触发change事件
const input = document.getElementById('input')
let timer = null

input.addEventListerner('keyup',function(){
    if(timer){
        clearTimeout(timer)
    }
    timer = setTimeout(()=>{
        //模拟触发change事件
        console.log(input.value)
        
        //清空定时器
        timer = null
    },500)
})
//防抖
function debounce(fn,delay = 500){
    // timer是在闭包中
    let timer = null
    return function(){
    	if(timer){
    		clearTimeout(timer)
    	}
    	timer = setTimeout(()=>{
    		fn.apply(this,arguments)
    		timer=null
		},delay)
    }
}
input.addEventListener('keyup',debounce(function(e){
	console.log(e.target,input.value)
},300))

手写节流 throttle

  • 拖拽一个元素时,要随时拿到该元素被拖拽的位置
  • 直接用drag事件,则会频繁触发,很容易导致卡顿
  • 节流: 无论拖拽速度多快,都会间隔100ms触发一次
const div1 = document.getElementById('div1')
let timer = null

div1.addEventListerner('drag',function(){
    if(timer){
        return   //跟防抖的区别
    }
    timer = setTimeout(()=>{
        console.log(e.offsetX,e.offsetY)
        
        //清空定时器
        timer = null
    },100)
})

// 封装节流函数throttle
function throttle(fn,delay=100){
	let timer =null
	return function(){
		if(timer){
			return
		}
		timer = setTimeout(()=>{
			fn.apply(this,arguments)
			timer=null
		},delay)
	}
}
div1.addEventListener('drag',throttle(function(e){
	console.log(e.offsetX,e.offsetY)
},200))