原则
- 多使用内存、缓存或其他方法
- 减少 CPU 计算量,减少网络加载耗时
- 适用于所有编程的性能优化-空间换时间
从何入手
- 让加载更快
- 减少资源体积:压缩代码
- 减少访问次数:合并代码、SSR 服务端渲染、缓存
- 使用更快的网络:CDN
- 让渲染更快
- css 放在 head, js 放在 body 最下面
- 尽早开始执行 js,用 DOMContentLoaded 出发
- 懒加载(图片懒加载,上滑加载更多)
- 对 DOM 查询进行缓存
- 频繁 DOM 操作,合并到一起插入 DOM 结构
- 节流 throttle 防抖 debounce,让渲染更加流畅
示例
资源合并
缓存
- 静态资源加 hash 后缀,根据文件内容计算 hash
- 文件内容不变,则 hash 不变,则 url 不变
- url 和文件不变,则会自动出发 http 缓存机制,返回 304
配置 webpack 打包时,根据文件内容生成 hash 后缀
SSR
服务器渲染:将网页和数据一起加载,一起渲染
非SSR(前后端分离):先加载网页,再加载数据,再渲染数据
早先的 JSP ASP PHP ,现在的 vue react SSR
懒加载
1 2 3 4 5 6
| <img id="img1" src="preview.png" data-realsrc="abc.png"> <script> var img1 = document.getElementById('img1') img1.src = img1.getAttribute('data-realsrc') </script>
|
参考
缓存 DOM 查询
1 2 3 4 5 6 7 8 9 10 11
| for(let i = 0;i<document.getElementById('div1').length;i++){ }
let list = document.getElementById('div1') const l = list.length for(let i = 0;i<l;i++){ }
|
多个DOM操作一起插入到DOM结构
1 2 3 4 5 6 7 8 9 10 11 12 13
| let list = document.getElementById('div1')
const frag = document.createDocumentFragment()
for(let i = 0;i<10;i++){ const li = document.createElement("li") li.innerHTML = "list item" + i frag.appendChild(li) }
list.appendChild(frag)
|
尽早开始js执行
1 2 3 4 5 6
| window.addEventListener('load', function(){ }) document.addEventListener('DOMContentLoaded', function(){ })
|
防抖
应用场景:监听一个输入框的,文字变化后出发change时间,直接用keyup事件,则会频繁触发change事件,防抖:用户输入结束活暂停时,才会触发change,减少请求次数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <input type="text" id="input1"> <script> const input1 = document.getElementById('input1')
let timer
input1.addEventListener('keyup',function(){ if(timer){ clearTimeout(timer) } timer = setTimeout(()=>{ console.log(input1.value) timer = null },500) }) </script>
|
封装防抖函数,返回是一个函数没所以形成了一个闭包,有时候要传this和参数,所以不要使用箭头函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| function debounce(fn, delay = 500){ let timer = null
return function(){ if(timer){ clearTimeout(timer) } timer = setTimeout(()=>{ fn.apply(this, arguments) timer = null },delay) } } input1.addEventListener('keyup', debounce(function(){ console.log(input1.value) }), 600)
|
节流 throttle
应用场景:拖拽一个元素时,要随时拿到该元素被拖拽的位置,直接用drag事件,则会频繁出发,很容易导致卡顿,节流:无论拖拽速度多快,都会每隔100ms触发一次
1 2 3 4 5 6 7 8 9 10 11 12 13
| <div id="div1" draggable="true"></div> <script> const div1 = document.getElementById('div1') let timer = null div1.addEventListener('drag', function(e){ if(timer){ return } timer = setTimeout(()=>{ console.log(e.offsetX, e.offsetY) },100) }) </script>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| function throttle(fn, delay = 100){ let timer = null return function(){ if(timer){ return } timer = setTimeout(()=>{ fn.apply(this, arguments) },delay) } } div1.addEventListener('drag',throttle(function(e){ console.log(e.offsetX, e.offsetY) }),200)
|
防抖和节流区别
参考
- 防抖(debounce):触发高频事件后 n 秒内函数只会执行一次,如果 n 秒内高频事件再次被触发,则重新计算时间
举例:就好像在百度搜索时,每次输入之后都有联想词弹出,这个控制联想词的方法就不可能是输入框内容一改变就触发的,他一定是当你结束输入一段时间之后才会触发。
- 节流(thorttle):高频事件触发,但在 n 秒内只会执行一次,所以节流会稀释函数的执行频率
举例:预定一个函数只有在大于等于执行周期时才执行,周期内调用不执行。就好像你在淘宝抢购某一件限量热卖商品时,你不断点刷新点购买,可是总有一段时间你点上是没有效果,这里就用到了节流,就是怕点的太快导致系统出现bug。
- 区别:防抖动是将多次执行变为最后一次执行,节流是将多次执行变成每隔一段时间执行。