Open zhuanyongxigua opened 6 years ago
节流和防抖其实差不多,从编程的角度说,都是防止一个函数被过多地调用。都是降低频率,只是程度不同,防抖降的很彻底,彻底到只有一次,而节流还是会多次,只不过是我们可以接受的多次。
把持续发生的事情堵住,只处理最后一次。
防抖的应用场景:文本输入验证(连续输入AJAX请求进行验证,验证一次就可以了)。
防抖的最简单的实现:
function debounce(method, context) { clearTimeout(method.tId); method.tId = setTimeout(function() { method.call(context); }, 1000); } function print() { console.log('hello world'); } window.onscroll = function() { debounce(print); };
把持续发生的事情堵住,每隔一段时间处理一次。
比如做一个拖拽的交互,如果每移动一个像素都执行一遍回调,这个性能消耗是惊人的。比较好的处理方法就是降低回调的频率(更准确的说是降低拖拽交互的频率,回调还是一直都在放生的)。
节流其他的应用场景:搜索联想(keyup);监听滚动事件判断是否到达页面底部,然后自动加载更多。
节流的实现: 第一种方法,当 scroll 事件刚触发时,打印一个 hello world,然后设置个 1000ms 的定时器,此后每次触发 scroll 事件触发回调,如果已经存在定时器,则回调不执行方法,直到定时器触发,handler 被清除,然后重新设置定时器。:
function Throttle(method, context) { if (method.tId) return; method.tId = setTimeout(function() { method.call(context); clearTimeout(method.tId); method.tId = false }, 1000); } function print() { console.log('hello world'); } window.onscroll = function() { Throttle(print); };
第二种方法,是用时间戳来判断是否已到回调该执行时间,记录上次执行的时间戳,然后每次触发 scroll 事件执行回调,回调中判断当前时间戳距离上次执行时间戳的间隔是否已经到达 1000ms,如果是,则执行,并更新上次执行的时间戳,如此循环;:
var stamp = Date.now(); function Throttle(method, context) { if (Date.now() - stamp < 1000) return; method.call(context); stamp = Date.now(); } function print() { console.log('hello world'); } window.onscroll = function() { Throttle(print); };
延迟加载使用的较多的就是图片的延迟加载,没有访问到的图片不会加载,会给服务器省流量的钱。
一个比较简单的思路:
在HTML标签中,img标签是这样的:
<img src="" data-src="https://.....">
data-src属性中放的是真实的地址,这样图片目前是不会加载的,
data-src
具体的执行的函数:
function lazyload() { const images = document.getElementsByTagName('img') const len = images.length let n = 0 return function() { const seeHeight = document.documentElement.clientHeight const scrollTop = document.documentElement.scrollTop || document.body.scrollTop for (let i = n; i < len; i++) { if (images[i].offsetTop < seeHeight + scrollTop) { if (images[i].getAttribute('src') === 'https://8.url.cn/edu/lego_modules/edu-ui/0.0.1/img/nohash/loading.gif') { images[i].src = images[i].getAttribute('data-src') } n = n + 1 } } } } var loadImages = lazyload() window.onload = function () { loadImages() window.addEventListener('scroll', loadImages, false) }
等到window.onload触发,或者滚动事件触发的时候,把data=src中的路径放到src里面去。
window.onload
data=src
src
其实还可以做的更好,加一个有效性验证:
function lazyload() { const images = document.getElementsByTagName('img') const len = images.length let n = 0 return function() { const seeHeight = document.documentElement.clientHeight const scrollTop = document.documentElement.scrollTop || document.body.scrollTop for (let i = n; i < len; i++) { if (images[i].offsetTop < seeHeight + scrollTop) { if (images[i].getAttribute('src') === 'https://8.url.cn/edu/lego_modules/edu-ui/0.0.1/img/nohash/loading.gif') { // 有效性验证 var curImg = new Image curImg.src = images[i].getAttribute('data-src') // 如果地址找不到图片,就不会触发onload curImg.onload = function() { images[i].src = images[i].getAttribute('data-src') // 释放内存 curImg = null; } } n = n + 1 } } } } var loadImages = lazyload() window.onload = function () { loadImages() window.addEventListener('scroll', loadImages, false) }
对于预加载,会用到的地方,如”查看更多“的功能,可以在空闲的时候先把“查看更多”的内容拿到,点击了之后直接显示就可以了。
一般是出现在移动端的无线列表。
例子:
var data = []; var limit = 1e5; for (var i = 1; i < limit; i++) data.push(i); var $list = document.getElementById('list'); function update() { item_height = 24; $list.style.height = (data.length * item_height) + 'px'; var show_counts = Math.ceil(window.innerHeight / item_height) + 10; var update_list = function() { var scrollTop = (document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop; var start = Math.max(Math.floor(scrollTop / item_height) - 3, 0); var show_data = data.slice(start, start + show_counts); var html = ''; // 占位符,用于是滚动条正常使用 if (start !== 0) { html += `<li style="height:${start * item_height}px"></li>`; } show_data.forEach(v => { html += `<li>${v}</li>`; }); $list.innerHTML = html; }; window.onscroll = throttle(update_list, 100); update_list(); }
完整的例子在这里。
考虑了滚动条的位置。DOM中有一个占位符,在<li>的最上面,这个占位符的高度与已经划过去的列表的高度相同,目的是就是占用之前的列表的位置,从而使当前的滚动条的状态不变,划过去的列表实际上已经不在了,这样做的目的是节省资源,不会渲染太多的东西。
<li>
其实就是减少可视区域外的DOM数量。可视区域外的用占位符代替。没有移除的就容易卡顿,如果数据不多的话问题不大。
节流和防抖
节流和防抖其实差不多,从编程的角度说,都是防止一个函数被过多地调用。都是降低频率,只是程度不同,防抖降的很彻底,彻底到只有一次,而节流还是会多次,只不过是我们可以接受的多次。
防抖
把持续发生的事情堵住,只处理最后一次。
防抖的应用场景:文本输入验证(连续输入AJAX请求进行验证,验证一次就可以了)。
防抖的最简单的实现:
节流
把持续发生的事情堵住,每隔一段时间处理一次。
比如做一个拖拽的交互,如果每移动一个像素都执行一遍回调,这个性能消耗是惊人的。比较好的处理方法就是降低回调的频率(更准确的说是降低拖拽交互的频率,回调还是一直都在放生的)。
节流其他的应用场景:搜索联想(keyup);监听滚动事件判断是否到达页面底部,然后自动加载更多。
节流的实现: 第一种方法,当 scroll 事件刚触发时,打印一个 hello world,然后设置个 1000ms 的定时器,此后每次触发 scroll 事件触发回调,如果已经存在定时器,则回调不执行方法,直到定时器触发,handler 被清除,然后重新设置定时器。:
第二种方法,是用时间戳来判断是否已到回调该执行时间,记录上次执行的时间戳,然后每次触发 scroll 事件执行回调,回调中判断当前时间戳距离上次执行时间戳的间隔是否已经到达 1000ms,如果是,则执行,并更新上次执行的时间戳,如此循环;:
延迟加载和预加载
延迟加载使用的较多的就是图片的延迟加载,没有访问到的图片不会加载,会给服务器省流量的钱。
一个比较简单的思路:
在HTML标签中,img标签是这样的:
data-src
属性中放的是真实的地址,这样图片目前是不会加载的,具体的执行的函数:
等到
window.onload
触发,或者滚动事件触发的时候,把data=src
中的路径放到src
里面去。其实还可以做的更好,加一个有效性验证:
对于预加载,会用到的地方,如”查看更多“的功能,可以在空闲的时候先把“查看更多”的内容拿到,点击了之后直接显示就可以了。
大列表优化
一般是出现在移动端的无线列表。
例子:
完整的例子在这里。
考虑了滚动条的位置。DOM中有一个占位符,在
<li>
的最上面,这个占位符的高度与已经划过去的列表的高度相同,目的是就是占用之前的列表的位置,从而使当前的滚动条的状态不变,划过去的列表实际上已经不在了,这样做的目的是节省资源,不会渲染太多的东西。其实就是减少可视区域外的DOM数量。可视区域外的用占位符代替。没有移除的就容易卡顿,如果数据不多的话问题不大。